require "uri" require "commands/status" require "commands/bootstrap_templates" require "lib/executors/server_executor" require "providers/provider_factory" require "db/mongo/models/server" require "workers/create_server_worker" require "workers/bootstrap_worker" require "app/api2/parsers/server" require_relative "request_handler" module Devops module API2_0 module Handler class Server < RequestHandler set_parser Devops::API2_0::Parser::ServerParser extend StatusCommands extend BootstrapTemplatesCommands #scheduler = Rufus::Scheduler.new def servers fields, reserved = parser.servers Devops::Db.connector.servers(nil, nil, nil, reserved, fields).map {|s| s.to_hash} end def chef_servers KnifeFactory.instance.chef_node_list end def provider_servers provider provider_servers_with_account provider, nil end def provider_servers_with_account provider, account ::Provider::ProviderFactory.get(provider, account).servers end def server id get_server_by_key(id, parser.server) end def delete id s = get_server_by_key(id, parser.instance_key) ### Authorization Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user Devops::Executor::ServerExecutor.new(s, "").delete_server 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.deploy_env = 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 node_name call_deploy(node_name) do |options| [ Worker.start_async(DeployWorker, @request, options) ] end end def deploy_stream node_name, out call_deploy(node_name) do |options| Worker.start_sync(DeployWorker, @request, options, out) end rescue RecordNotFound => e out << e.message -10 end def call_deploy node_name body = parser.deploy names = body["names"] tags = body["tags"] || [] run_list = body["run_list"] dir = DevopsConfig.config[:report_dir_v2] owner = parser.current_user s = Server.get_server_by_key(node_name, parser.instance_key) project = Devops::Db.connector.check_project_auth s.project, s.deploy_env, owner deploy_info = create_deploy_info(s, project, body["build_number"]) deploy_info["run_list"] = run_list if run_list yield({ server_attrs: s.to_hash, owner: owner, tags: tags, deploy_info: deploy_info }) end def pause_server node_name s = get_server_by_key(node_name, parser.instance_key) ## Authorization Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user provider = s.provider_instance r = provider.pause_server s if r.nil? set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::PAUSE) "Server with instance ID '#{s.id}' and node name '#{node_name}' is paused" else raise ConflictException.new("Server with instance ID '#{s.id}' and node name '#{node_name}' can not be paused, It in state '#{r}'", 409) end end def unpause_server node_name s = get_server_by_key(node_name, parser.instance_key) ## Authorization Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user provider = s.provider_instance r = provider.unpause_server s if r.nil? set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::UNPAUSE) "Server with instance ID '#{s.id}' and node name '#{node_name}' is unpaused" else raise ConflictException.new("Server with instance ID '#{s.id}' and node name '#{node_name}' can not be unpaused, It in state '#{r}'") end end def reserve_server node_name s = get_server_by_key(node_name, parser.instance_key) user = parser.current_user Devops::Db.connector.check_project_auth s.project, s.deploy_env, user raise ConflictException.new("Server '#{node_name}' already reserved") unless s.reserved_by.nil? s.reserved_by = user set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::RESERVE) end def unreserve_server node_name s = get_server_by_key(node_name, parser.instance_key) Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user raise ConflictException.new("Server '#{node_name}' is not reserved") if s.reserved_by.nil? s.reserved_by = nil set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::UNRESERVE) end # TODO: check bootstrap template name def bootstrap_server_stream out options = prepare_bootstrap_server s = options.delete(:server) status = [] cert = Devops::Db.connector.key s.key DevopsLogger.logger.debug "Bootstrap certificate path: #{cert.path}" #bootstrap s, out, cert.path, logger options[:cert_path] = cert.path executor = Devops::Executor::ServerExecutor.new(s, out) r = executor.two_phase_bootstrap(options) str = nil r = if check_server(s) Devops::Db.connector.server_set_chef_node_name s str = "Server with id '#{s.id}' is bootstraped" DevopsLogger.logger.info str 0 else str = "Server with id '#{s.id}' is not bootstraped" DevopsLogger.logger.warn str 1 end status.push r out << str out << "\n" status end def bootstrap_server server, rl, bootstrap_template = prepare_bootstrap_server jid = Worker.start_async(BootstrapWorker, server_attrs: server.to_mongo_hash, bootstrap_template: bootstrap_template, owner: parser.current_user ) sleep 1 [jid] end def prepare_bootstrap_server body = parser.bootstrap id = body["instance_id"] name = body["name"] rl = body["run_list"] t = body["bootstrap_template"] s = Devops::Db.connector.server_by_instance_id(id) res = {server: s} p = Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user d = p.deploy_env(s.deploy_env) provider = s.provider_instance check_chef_node_name(name, provider, KnifeFactory.instance) unless name.nil? unless t.nil? templates = get_templates halt_response("Invalid bootstrap template '#{t}', available values: #{templates.join(", ")}", 400) unless templates.include?(t) res[:bootstrap_template] = t end s.chef_node_name = name || provider.create_default_chef_node_name(s) res[:run_list] = rl || d.run_list return res end def unbootstrap_server id s = get_server_by_key(id, parser.instance_key) ### Authorization Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user Devops::Executor::ServerExecutor.new(s, "").unbootstrap end def add_server body = parser.add_server project = body["project"] deploy_env = body["deploy_env"] p = Devops::Db.connector.check_project_auth project, deploy_env, parser.current_user d = p.deploy_env(deploy_env) cert = Devops::Db.connector.key(body["key"]) provider = ::Provider::ProviderFactory.get("static") s = Devops::Model::Server.new s.provider = provider.name s.project = project s.deploy_env = deploy_env s.remote_user = body["remote_user"] s.private_ip = body["private_ip"] s.public_ip = body["public_ip"] s.id = "static_#{cert.id}-#{Time.now.to_i}" s.key = cert.id Devops::Db.connector.server_insert s "Server '#{s.id}' has been added" end def set_tags node_name tags = parser.tags prepare_tags do |id, provider| provider.set_tags id, tags end end def unset_tags node_name tags = parser.tags prepare_tags do |id, provider| provider.unset_tags id, tags end end def prepare_tags node_name s = get_server_by_key(node_name, parser.instance_key) Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user provider = ::Provider::ProviderFactory.get(s.provider) yield s.id, provider end def set_run_list node_name s = get_server_by_key(node_name, parser.instance_key) Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user Devops::Db.connector.set_server_run_list(s.id, parser.run_list) end def get_server_by_key id, key mongo = Devops::Db.connector key == "instance" ? mongo.server_by_instance_id(id) : mongo.server_by_chef_node_name(id) end def check_chef_node_name name, provider, knife Devops::Db.connector.server_by_chef_node_name(name) raise InvalidRecord.new("Server with name '#{name}' already exist") rescue RecordNotFound => e # server not found - OK s = provider.servers.detect {|s| s["name"] == name} raise InvalidRecord.new("#{provider.name} node with name '#{name}' already exist") unless s.nil? s = knife.chef_node_list.detect {|n| n == name} raise InvalidRecord.new("Chef node with name '#{name}' already exist") unless s.nil? s = knife.chef_client_list.detect {|c| c == name} raise InvalidRecord.new("Chef client with name '#{name}' already exist") unless s.nil? end private def check_if_server_attrs_are_valid(body, user) key_name = body["key"] Devops::Db.connector.key(key_name) unless key_name.nil? project = Devops::Db.connector.check_project_auth(body["project"], body["deploy_env"], user) env = project.deploy_env(body["deploy_env"]) 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 def set_last_operation_type_and_save(server, operation_type) server.set_last_operation(operation_type) Devops::Db.connector.server_update(server) end end end end end