diff --git a/devops-client/lib/devops-client/handler/stack.rb b/devops-client/lib/devops-client/handler/stack.rb index e251ccb..a4ae3f0 100644 --- a/devops-client/lib/devops-client/handler/stack.rb +++ b/devops-client/lib/devops-client/handler/stack.rb @@ -46,6 +46,7 @@ class Stack < Handler q[:project] = options[:project] || resources_selector.select_available_project q[:deploy_env] = options[:deploy_env] || enter_parameter(I18n.t('handler.stack.create.deploy_env')) q[:stack_template] = options[:stack_template] || resources_selector.select_available_stack_template(provider: q[:provider]) + q[:run_list] = options[:run_list] || enter_parameter(I18n.t('handler.stack.create.run_list')) filepath = options[:parameters_file] || enter_parameter(I18n.t('handler.stack.create.parameters_file')) q[:parameters] = JSON.parse(File.read(filepath)) diff --git a/devops-client/lib/devops-client/options/stack_options.rb b/devops-client/lib/devops-client/options/stack_options.rb index e52c894..06d3770 100644 --- a/devops-client/lib/devops-client/options/stack_options.rb +++ b/devops-client/lib/devops-client/options/stack_options.rb @@ -25,6 +25,7 @@ class StackOptions < CommonOptions parser.recognize_option_value(:deploy_env, 'stack') parser.recognize_option_value(:stack_template, 'stack') parser.recognize_option_value(:parameters_file, 'stack') + parser.recognize_option_value(:run_list, 'stack') end end diff --git a/devops-client/locales/en.yml b/devops-client/locales/en.yml index 2615061..e388b8a 100644 --- a/devops-client/locales/en.yml +++ b/devops-client/locales/en.yml @@ -115,6 +115,7 @@ en: id: "Id: " deploy_env: "Deploy env: " parameters_file: "Path to file with JSON parameters: " + run_list: Run list question: create: "Are you sure to create stack?" delete: "Are you sure to delete stack '%{name}'?" diff --git a/devops-service/Gemfile b/devops-service/Gemfile index 2b14f50..91f848c 100644 --- a/devops-service/Gemfile +++ b/devops-service/Gemfile @@ -21,7 +21,7 @@ gem 'rack-accept-media-types' gem 'rack', '1.5.2' gem 'hooks' -# gem "devops-nibr", :path => "plugins/devops-nibr" +#gem "devops-nibr", :path => "../../devops-nibr" group :test do gem 'cucumber' diff --git a/devops-service/app/api2/handlers/bootstrap_templates.rb b/devops-service/app/api2/handlers/bootstrap_templates.rb index 94ffa6b..4c4e8f8 100644 --- a/devops-service/app/api2/handlers/bootstrap_templates.rb +++ b/devops-service/app/api2/handlers/bootstrap_templates.rb @@ -1,9 +1,13 @@ require "commands/bootstrap_templates" +require "app/api2/parsers/bootstrap_template" +require_relative "request_handler" module Devops module API2_0 module Handler - class BootstrapTemplates + class BootstrapTemplates < RequestHandler + + set_parser Devops::API2_0::Parser::BootstrapTemplateParser include BootstrapTemplatesCommands diff --git a/devops-service/app/api2/handlers/deploy.rb b/devops-service/app/api2/handlers/deploy.rb index 491b564..2360253 100644 --- a/devops-service/app/api2/handlers/deploy.rb +++ b/devops-service/app/api2/handlers/deploy.rb @@ -1,51 +1,100 @@ require "commands/deploy" require "commands/status" require "workers/deploy_worker" +require "exceptions/deploy_info_error" +require "app/api2/parsers/deploy" +require_relative "request_handler" module Devops module API2_0 module Handler - class Deploy + class Deploy < RequestHandler extend DeployCommands extend StatusCommands - def initialize req, params - @request = req - @params = params - end + set_parser Devops::API2_0::Parser::DeployParser - def deploy names, tags + def deploy + body = parser.deploy + names = body["names"] + tags = body["tags"] || [] dir = DevopsConfig.config[:report_dir_v2] files = [] - uri = URI.parse(@request.url) + jid = nil + uri = URI.parse(parser.request.url) + owner = parser.current_user + @deploy_info_buf = {} servers(names).each do |s| project = begin - Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + Devops::Db.connector.check_project_auth s.project, s.deploy_env, owner rescue InvalidPrivileges, RecordNotFound => e + DevopsLogger.logger.warn e.message next end - jid = DeployWorker.perform_async(dir, s.to_hash, tags, @request.env['REMOTE_USER'], DevopsConfig.config) + begin + deploy_info = create_deploy_info(s, project, body["build_number"]) + jid = DeployWorker.perform_async(dir, s.to_hash, tags, owner, DevopsConfig.config, deploy_info) + Worker.set_status jid, Worker::STATUS::IN_QUEUE + DevopsLogger.logger.info "Job '#{jid}' has been queued" + rescue DeployInfoError => e + msg = "Can not get deploy info: " + e.message + DevopsLogger.logger.error msg + jid = "error_#{s.chef_node_name}_#{Time.new.to_i}" + file = File.jon(dir, jid) + File.open(file, "w") do |out| + #unless attributes.nil? + # out << "Report attributes: #{attributes.inspect}\n" + #end + out.write msg + end + o = { + "file" => file, + "_id" => jid, + "created_by" => owner, + "project" => s.project, + "deploy_env" => s.deploy_env, + "type" => Report::DEPLOY_TYPE, + "status" => Worker::STATUS::FAILED +# "attributes" => attributes + } + Devops::Db.connector.save_report(Report.new(o)) + end files.push jid end files end - def deploy_stream out, names, tags + def deploy_stream out + body = parser.deploy + names = body["names"] + tags = body["tags"] || [] status = [] + owner = parser.current_user + @deploy_info_buf = {} servers(names).each do |s| project = begin - Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + Devops::Db.connector.check_project_auth s.project, s.deploy_env, owner rescue InvalidPrivileges, RecordNotFound => e out << e.message + "\n" status.push 2 next end - res = deploy_server_proc.call(out, s, tags) - status.push(res) + begin + deploy_info = create_deploy_info(s, project, body["build_number"]) + res = deploy_server_proc.call(out, s, tags, deploy_info) + status.push(res) + rescue DeployInfoError => e + msg = "Can not get deploy info: " + e.message + DevopsLogger.logger.error msg + out << msg + "\n" + status.push 2 + next + end end - out << create_status(status) + status rescue RecordNotFound => e out << e.message + [-10] end def servers names @@ -54,6 +103,15 @@ module Devops servers.sort_by!{|s| names.index(s.chef_node_name)} servers end + + def create_deploy_info server, project, build_number + buf_key = "#{server.project}_#{server.deploy_env}" + deploy_info = if @deploy_info_buf.key?(buf_key) + @deploy_info_buf[buf_key] + else + @deploy_info_buf[buf_key] = project.deploy_info(server.deploy_env, build_number) + end + end end end end diff --git a/devops-service/app/api2/handlers/filter.rb b/devops-service/app/api2/handlers/filter.rb index 8a4cf23..8b8d73e 100644 --- a/devops-service/app/api2/handlers/filter.rb +++ b/devops-service/app/api2/handlers/filter.rb @@ -1,3 +1,4 @@ +require "app/api2/parsers/filter" require_relative "request_handler" module Devops @@ -5,16 +6,18 @@ module Devops module Handler class Filter < RequestHandler + set_parser Devops::API2_0::Parser::FilterParser + def available_images provider Devops::Db.connector.available_images(provider) end - def add_images images, provider - Devops::Db.connector.add_available_images(images, provider) + def add_images provider + Devops::Db.connector.add_available_images(parser.images, provider) end - def delete_images images, provider - Devops::Db.connector.delete_available_images(images, provider) + def delete_images provider + Devops::Db.connector.delete_available_images(parser.images, provider) end end diff --git a/devops-service/app/api2/handlers/flavor.rb b/devops-service/app/api2/handlers/flavor.rb index 7c36091..421bb1a 100644 --- a/devops-service/app/api2/handlers/flavor.rb +++ b/devops-service/app/api2/handlers/flavor.rb @@ -6,13 +6,8 @@ module Devops module Handler class Flavor < RequestHandler - def initialize request, params - @provider = params[:provider] - end - - def flavors - p = ::Provider::ProviderFactory.get @provider - p.flavors + def flavors provider + ::Provider::ProviderFactory.get(provider).flavors end end diff --git a/devops-service/app/api2/handlers/group.rb b/devops-service/app/api2/handlers/group.rb index 2016d88..26f2e36 100644 --- a/devops-service/app/api2/handlers/group.rb +++ b/devops-service/app/api2/handlers/group.rb @@ -1,17 +1,14 @@ require "providers/provider_factory" +require_relative "request_handler" module Devops module API2_0 module Handler - class Group + class Group < RequestHandler - def initialize provider - @provider = provider - end - - def groups params - p = ::Provider::ProviderFactory.get @provider - p.groups(params) + # TODO: vpc support for ec2 + def groups provider + ::Provider::ProviderFactory.get(provider).groups()#params end end end diff --git a/devops-service/app/api2/handlers/image.rb b/devops-service/app/api2/handlers/image.rb index 134939e..60bbbe1 100644 --- a/devops-service/app/api2/handlers/image.rb +++ b/devops-service/app/api2/handlers/image.rb @@ -1,5 +1,6 @@ require "providers/provider_factory" require "commands/image" +require "app/api2/parsers/image" require_relative "request_handler" module Devops @@ -7,10 +8,12 @@ module Devops module Handler class Image < RequestHandler + set_parser Devops::API2_0::Parser::ImageParser + extend ImageCommands - def images provider - Devops::Db.connector.images(provider) + def images + Devops::Db.connector.images(parser.images) end def provider_images provider @@ -21,13 +24,13 @@ module Devops Devops::Db.connector.image(id) end - def create_image image - Devops::Db.connector.image_insert Devops::Model::Image.new(image) + def create_image + Devops::Db.connector.image_insert parser.image end - def update_image id, image + def update_image id Devops::Db.connector.image id - obj = Devops::Model::Image.new(image) + obj = parser.image obj.id = id Devops::Db.connector.image_update obj end diff --git a/devops-service/app/api2/handlers/key.rb b/devops-service/app/api2/handlers/key.rb index 4779120..2cfd872 100644 --- a/devops-service/app/api2/handlers/key.rb +++ b/devops-service/app/api2/handlers/key.rb @@ -1,17 +1,24 @@ require "db/mongo/models/key" require "fileutils" +require "app/api2/parsers/key" +require_relative "request_handler" module Devops module API2_0 module Handler - class Key + class Key < RequestHandler + + set_parser Devops::API2_0::Parser::KeyParser def keys - keys = Devops::Db.connector.keys.map {|i| i.to_hash} - keys.each {|k| k.delete("path")} # We should not return path to the key + Devops::Db.connector.keys({}, {path: false}) end - def create body, file_name + def create + body = parser.create + fname = body["file_name"] + file_name = File.join(settings.keys_dir, fname) + raise InvalidRecord.new("File '#{fname}' already exist") if File.exists?(file_name) File.open(file_name, "w") do |f| f.write(body["content"]) f.chmod(0400) @@ -23,19 +30,19 @@ module Devops def delete key_id mongo = Devops::Db.connector - servers = mongo.servers_by_key key_id - unless servers.empty? - s_str = servers.map{|s| s.id}.join(", ") - raise DependencyError.new "Deleting is forbidden: Key is used in servers: #{s_str}" - end + servers = mongo.servers_by_key key_id + unless servers.empty? + s_str = servers.map{|s| s.id}.join(", ") + raise DependencyError.new "Deleting is forbidden: Key is used in servers: #{s_str}" + end - k = mongo.key key_id - begin - FileUtils.rm(k.path) - rescue - DevopsLogger.logger.error "Missing key file for #{key_id} - #{k.filename}" - end - mongo.key_delete key_id + k = mongo.key key_id + begin + FileUtils.rm(k.path) + rescue + DevopsLogger.logger.error "Missing key file for #{key_id} - #{k.filename}" + end + mongo.key_delete key_id end end diff --git a/devops-service/app/api2/handlers/project.rb b/devops-service/app/api2/handlers/project.rb index d59fd1f..e61536d 100644 --- a/devops-service/app/api2/handlers/project.rb +++ b/devops-service/app/api2/handlers/project.rb @@ -3,6 +3,8 @@ require "commands/status" require "commands/server" require "db/mongo/models/project" require "workers/project_test_worker" +require "app/api2/parsers/project" +require "lib/project/type/types_factory" require_relative "../helpers/version_2.rb" require_relative "request_handler" @@ -10,21 +12,21 @@ module Devops module API2_0 module Handler class Project < RequestHandler + + set_parser Devops::API2_0::Parser::ProjectParser + include Devops::API2_0::Helpers extend DeployCommands extend StatusCommands extend ServerCommands + def project_types + Devops::TypesFactory.types_names + end + def projects - fields = [] - if @params.key?("fields") and @params["fields"].is_a?(Array) - Devops::Model::Project.fields.each do |k| - fields.push k if @params["fields"].include?(k) - end - end - archived = @params.include?("archived") - Devops::Db.connector.projects(nil, nil, fields, archived) + Devops::Db.connector.projects(nil, nil, parser.projects, parser.archived_projects) end def project id @@ -34,44 +36,46 @@ module Devops def project_servers id # check if project exists Devops::Db.connector.project(id) - Devops::Db.connector.servers(id, @params[:deploy_env]) + Devops::Db.connector.servers(id, parser.project_servers) end def project_stacks id # check if project exists Devops::Db.connector.project(id) - options = {project: @params[:project]} - options[:deploy_env] = @params[:deploy_env] if @params[:deploy_env] + options = {project: id} + deploy_env = parser.project_stacks + options[:deploy_env] = deploy_env if deploy_env Devops::Db.connector.stacks(options) end # TODO: multi project - def create_project body - p = Devops::Model::Project.new(body) + def create_project + p = parser.create_project raise InvalidRecord.new("Project '#{p.id}' already exist") if Devops::Db.connector.is_project_exists?(p) - p.add_authorized_user [@request.env['REMOTE_USER']] + p.add_authorized_user [parser.current_user] p.create if p.multi? "Project '#{p.id}' with type 'multi' created" else - roles = create_roles p.id, p.deploy_envs, DevopsLogger.logger + roles = create_roles p.id, p.deploy_envs "Project '#{p.id}' created. " + create_roles_response(roles) end end # TODO: multi project - def update_project id, body - project = Devops::Model::Project.new(body) + def update_project id + project = parser.update project.id = id old_project = Devops::Db.connector.project id Devops::Db.connector.project_update project - roles = create_new_roles(old_project, project, DevopsLogger.logger) + roles = create_new_roles(old_project, project) create_roles_response(roles) end # TODO: multi project - def update_project_users id, deploy_env, users + def update_project_users id + deploy_env, users = parser.project_users project = Devops::Db.connector.project(id) dbusers = Devops::Db.connector.users(users).map{|u| u.id} buf = dbusers - users @@ -83,23 +87,31 @@ module Devops end # TODO: multi project - def delete_project_users id, deploy_env, users + def delete_project_users id + deploy_env, users = parser.project_users project = Devops::Db.connector.project(id) project.remove_authorized_user users, deploy_env Devops::Db.connector.project_update project "Users '#{users.join("', '")}' have been removed from '#{id}' project's authorized users" end - # TODO: multi project - def set_project_env_run_list id, deploy_env, list + def set_project_run_list id + list = parser.run_list project = Devops::Db.connector.project(id) - env = project.deploy_env deploy_env - env.run_list = list - Devops::Db.connector.project_update project - "Updated environment '#{env.identifier}' with run_list '#{env.run_list.inspect}' in project '#{project.id}'" + Devops::Db.connector.set_project_run_list id, list + "Updated project with run_list '#{list.inspect}'" end - def delete_project id, deploy_env + def set_project_env_run_list id, deploy_env + list = parser.run_list + project = Devops::Db.connector.project(id) + env = project.deploy_env deploy_env + Devops::Db.connector.set_project_env_run_list id, deploy_env, list + "Updated environment '#{env.identifier}' with run_list '#{list.inspect}' in project '#{project.id}'" + end + + def delete_project id + deploy_env = parser.delete servers = Devops::Db.connector.servers id raise DependencyError.new "Deleting #{id} is forbidden: Project has servers" if !servers.empty? project = Devops::Db.connector.project(id) @@ -113,7 +125,8 @@ module Devops end end - def deploy_project_stream out, id, deploy_env, servers, body + def deploy_project_stream out, id + deploy_env, servers = parser.deploy keys = {} dbserver = servers(id, deploy_env, servers) out << (dbservers.empty? ? "No reserved servers to deploy\n" : "Deploy servers: '#{dbservers.map{|s| s.chef_node_name}.join("', '")}'\n") @@ -135,7 +148,8 @@ module Devops status end - def deploy_project id, deploy_env, servers, body + def deploy_project id + deploy_env, servers = parser.deploy dir = DevopsConfig[:report_dir_v2] files = [] uri = URI.parse(request.url) @@ -173,7 +187,7 @@ module Devops def test_project id, deploy_env project = Devops::Db.connector.project(id) env = project.deploy_env deploy_env - #logger.info "Test project '#{project.id}' and environment '#{env.identifier}'" + DevopsLogger.logger.info "Test project '#{project.id}' and environment '#{env.identifier}'" if env.provider == ::Provider::Static::PROVIDER msg = "Can not test environment with provider '#{::Provider::Static::PROVIDER}'" Logger.warn msg @@ -189,13 +203,13 @@ module Devops } jid = ProjectTestWorker.perform_async(dir, p, DevopsConfig.config) Worker.set_status jid, Worker::STATUS::IN_QUEUE - #logger.info "Job '#{jid}' has been created" + DevopsLogger.logger.info "Job '#{jid}' has been created" uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid sleep 1 return [uri.to_s] end - def create_roles project_id, envs, logger + def create_roles project_id, envs all_roles = KnifeCommands.roles return " Can't get roles list" if all_roles.nil? roles = {:new => [], :error => [], :exist => []} @@ -207,21 +221,21 @@ module Devops else KnifeCommands.create_role role_name, project_id, e.identifier roles[:new].push role_name - logger.info "Role '#{role_name}' created" + DevopsLogger.logger.info "Role '#{role_name}' created" end rescue => er roles[:error].push role_name - logger.error "Role '#{role_name}' can not be created: #{er.message}" + DevopsLogger.logger.error "Role '#{role_name}' can not be created: #{er.message}" end end roles end - def create_new_roles old_project, new_project, logger + def create_new_roles old_project, new_project old_project.deploy_envs.each do |e| new_project.remove_env(e.identifier) end - create_roles new_project.id, new_project.deploy_envs, logger + create_roles new_project.id, new_project.deploy_envs end def create_roles_response roles diff --git a/devops-service/app/api2/handlers/report.rb b/devops-service/app/api2/handlers/report.rb index 1918d66..8121c2c 100644 --- a/devops-service/app/api2/handlers/report.rb +++ b/devops-service/app/api2/handlers/report.rb @@ -5,19 +5,16 @@ module Devops module Handler class Report < RequestHandler - def initialize request, params - @params = params - end - def options + params = @request.params options = {} ["project", "deploy_env", "type", "created_by", "date_from", "date_to", "sort", "status", "chef_node_name", "max_number"].each do |k| - options[k] = @params[k] unless @params[k].nil? - end - attributes_keys = @params.keys.select{|k| k =~ /attributes\.*/} - attributes_keys.each do |ak| - options[ak] = @params[ak] + options[k] = params[k] unless params[k].nil? end + #attributes_keys = params.keys.select{|k| k =~ /attributes\.*/} + #attributes_keys.each do |ak| + # options[ak] = params[ak] + #end options end diff --git a/devops-service/app/api2/handlers/request_handler.rb b/devops-service/app/api2/handlers/request_handler.rb index 8117c08..cf50caa 100644 --- a/devops-service/app/api2/handlers/request_handler.rb +++ b/devops-service/app/api2/handlers/request_handler.rb @@ -2,9 +2,17 @@ module Devops module API2_0 module Handler class RequestHandler - def initialize request, params + + class << self + def set_parser parser + define_method("parser") do + @request_parser ||= parser.new(@request) + end + end + end + + def initialize request @request = request - @params = params end private diff --git a/devops-service/app/api2/handlers/script.rb b/devops-service/app/api2/handlers/script.rb index eae8dfe..80ac4f4 100644 --- a/devops-service/app/api2/handlers/script.rb +++ b/devops-service/app/api2/handlers/script.rb @@ -1,6 +1,7 @@ require "providers/provider_factory" require "fileutils" require "commands/status" +require "app/api2/parsers/script" require_relative "request_handler" module Devops @@ -8,13 +9,16 @@ module Devops module Handler class Script < RequestHandler + set_parser Devops::API2_0::Parser::ScriptParser + def scripts res = [] Dir.foreach(DevopsConfig.config[:scripts_dir]) {|f| res.push(f) unless f.start_with?(".")} end - def execute_command out, cmd, node_name - user = @request.env['REMOTE_USER'] + def execute_command out, node_name + cmd = parse.execute_command + user = parser.current_user s = ::Devops::Db.connector.server_by_chef_node_name node_name ::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user cert = ::Devops::Db.connector.key s.key @@ -30,64 +34,65 @@ module Devops out << "\nDone" end - def run_script out, file_name, nodes, script_params - file = File.join(DevopsConfig.config[:scripts_dir], file_name) - unless File.exists?(file) - out << "File '#{file_name}' does not exist\n" - return + def run_script out, script_name + nodes, script_params = parser.run_script + file = File.join(DevopsConfig.config[:scripts_dir], script_name) + unless File.exists?(file) + out << "File '#{file_name}' does not exist\n" + return + end + servers = ::Devops::Db.connector.servers_by_names(nodes) + if servers.empty? + out << "No servers found for names '#{nodes.join("', '")}'\n" + return + end + user = @request.env['REMOTE_USER'] + servers.each do |s| + ::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user + end + status = [] + servers.each do |s| + cert = begin + ::Devops::Db.connector.key s.key + rescue + out << "No key found for '#{s.chef_node_name}'" + status.push 2 + next end - servers = ::Devops::Db.connector.servers_by_names(nodes) - if servers.empty? - out << "No servers found for names '#{nodes.join("', '")}'\n" - return + ssh_cmd = "ssh -i #{cert.path} #{s.remote_user}@#{s.public_ip || s.private_ip} 'bash -s' < %s" + out << "\nRun script on '#{s.chef_node_name}'\n" + unless script_params.nil? + ssh_cmd += " " + script_params.join(" ") end - user = @request.env['REMOTE_USER'] - servers.each do |s| - ::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user - end - status = [] - servers.each do |s| - cert = begin - ::Devops::Db.connector.key s.key - rescue - out << "No key found for '#{s.chef_node_name}'" - status.push 2 - next - end - ssh_cmd = "ssh -i #{cert.path} #{s.remote_user}@#{s.public_ip || s.private_ip} 'bash -s' < %s" - out << "\nRun script on '#{s.chef_node_name}'\n" - unless script_params.nil? - ssh_cmd += " " + script_params.join(" ") - end - out << (ssh_cmd % [file_name]) - out << "\n" + out << (ssh_cmd % [file_name]) + out << "\n" - begin - IO.popen( (ssh_cmd % [file]) + " 2>&1") do |so| - while line = so.gets do - out << line - end - so.close - status.push $?.to_i + begin + IO.popen( (ssh_cmd % [file]) + " 2>&1") do |so| + while line = so.gets do + out << line end - rescue IOError => e - logger.error e.message - out << e.message - status.push 3 + so.close + status.push $?.to_i end + rescue IOError => e + logger.error e.message + out << e.message + status.push 3 end - out << create_status(status) + end + status end def create_script file_name file = File.join(Devops::Api2.settings.scripts_dir, file_name) raise RecordNotFound.new("File '#{file_name}' already exist") if File.exists?(file) - File.open(file, "w") {|f| f.write(@request.body.read)} + File.open(file, "w") {|f| f.write(parser.create_script)} end def delete_script file_name file = File.join(Devops::Api2.settings.scripts_dir, file_name) - raise RecordNotFound.new("File '#{file_name}' does not exist", 404) unless File.exists?(file) + raise RecordNotFound.new("File '#{file_name}' does not exist") unless File.exists?(file) FileUtils.rm(file) end diff --git a/devops-service/app/api2/handlers/server.rb b/devops-service/app/api2/handlers/server.rb index 202d68b..fb50f57 100644 --- a/devops-service/app/api2/handlers/server.rb +++ b/devops-service/app/api2/handlers/server.rb @@ -12,11 +12,15 @@ 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 + class Server < RequestHandler + + set_parser Devops::API2_0::Parser::ServerParser extend StatusCommands extend ServerCommands @@ -24,7 +28,8 @@ module Devops scheduler = Rufus::Scheduler.new - def servers fields, reserved + def servers + fields, reserved = parser.servers Devops::DB.connector.servers(nil, nil, nil, reserved, fields).map {|s| s.to_hash} end @@ -37,44 +42,42 @@ module Devops end def server id - get_server_by_key(id, @params[:key]) + get_server_by_key(id, parse.server) end - def delete id, key - s = get_server_by_key(params[:id], key) + def delete id + s = get_server_by_key(id, parser.instance_key) ### Authorization - Devops::Db.connector.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - delete_server(s, Devops::Db.connector, logger) + Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user + delete_server(s, Devops::Db.connector, DevopsLogger.logger) end def create_server_stream out, body status = [] - prepare_create_server(body).each do |s| - res = create_server_proc.call(out, s, provider, Devops::Db.connector) + prepare_create_server do |project, env, user, body| + res = create_server(project, env, body, user, out) status.push res end status end - def create_server body + def create_server dir = DevopsConfig[:report_dir_v2] - files = [] - uri = URI.parse(request.url) - prepare_create_server(body).each do |s| - h = s.to_hash - h["options"] = s.options - jid = CreateServerWorker.perform_async(dir, env.provider, h, @request.env['REMOTE_USER'], DevopsConfig.config) + uri = URI.parse(parser.request.url) + file = prepare_create_server do |project, env, user, body| + jid = CreateServerWorker.perform_async(dir, body, user, DevopsConfig.config) Worker.set_status jid, Worker::STATUS::IN_QUEUE - #logger.info "Job '#{jid}' has been started" + DevopsLogger.logger.info "Job '#{jid}' has been started" uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid - files.push uri.to_s + uri.to_s end sleep 1 - files + [file] end - def prepare_create_server body - user = @request.env['REMOTE_USER'] + def prepare_create_server + body = parser.create + user = parser.current_user key_name = body["key"] new_key = Devops::Db.connector.key(key_name) unless key_name.nil? @@ -90,13 +93,15 @@ module Devops halt_response("Invalid security groups '#{buf.join("', '")}' for provider '#{provider.name}'") if buf.empty? end - Server.extract_servers(provider, p, env, body, user, Devops::Db.connector) + yield p, env, user, body + + #Server.extract_servers(provider, p, env, body, user) end - def pause_server node_name, key - s = Server.get_server_by_key(node_name, key) + def pause_server node_name + s = Server.get_server_by_key(node_name, parser.instance_key) ## Authorization - Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user provider = ::Provider::ProviderFactory.get(s.provider) r = provider.pause_server s if r.nil? @@ -106,10 +111,10 @@ module Devops end end - def unpause_server node_name, key - s = Server.get_server_by_key(params[:node_name], key) + def unpause_server node_name + s = Server.get_server_by_key(node_name, parser.instance_key) ## Authorization - Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user provider = ::Provider::ProviderFactory.get(s.provider) r = provider.unpause_server s if r.nil? @@ -119,25 +124,26 @@ module Devops end end - def reserve_server node_name, key - s = get_server_by_key(node_name, key) - Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + 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 Devops::Db.connector.server_update(s) end - def unreserve_server node_name, key - s = get_server_by_key(node_name, key) - Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + 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 Devops::Db.connector.server_update(s) end # TODO: check bootstrap template name - def bootstrap_server_stream out, body - s = prepare_create_server body + def bootstrap_server_stream out + s = prepare_create_server status = [] cert = Devops::Db.connector.key s.key logger.debug "Bootstrap certificate path: #{cert.path}" @@ -159,15 +165,15 @@ module Devops status end - def bootstrap_server body - s = prepare_create_server body + def bootstrap_server + s = prepare_bootstrap_server dir = DevopsConfig[:report_dir_v2] files = [] uri = URI.parse(@request.url) h = s.to_hash h["options"] = s.options h["_id"] = s.id - jid = BootstrapWorker.perform_async(dir, d.provider, h, @request.env['REMOTE_USER'], DevopsConfig.config) + jid = BootstrapWorker.perform_async(dir, d.provider, h, parser.current_user, DevopsConfig.config) Worker.set_status jid, Worker::STATUS::IN_QUEUE logger.info "Job '#{jid}' has been started" uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid @@ -178,12 +184,13 @@ module Devops files end - def prepare_bootstrap_server body + def prepare_bootstrap_server + body = parser.bootstrap id = body["instance_id"] name = body["name"] s = Devops::Db.connector.server_by_instance_id(id) - p = Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + p = Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user d = p.deploy_env s.deploy_env provider = ::Provider::ProviderFactory.get(s.provider) @@ -201,10 +208,11 @@ module Devops s end - def add_server body + 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, @request.env['REMOTE_USER'] + p = Devops::Db.connector.check_project_auth project, deploy_env, parser.current_user d = p.deploy_env(deploy_env) @@ -224,6 +232,33 @@ module Devops "Server '#{s.id}' has been added" end + def set_tags node_name + prepare_tags do |id, provider| + provider.set_tags id, parser.tags + end + end + + def unset_tags node_name + prepare_tags do |id, provider| + provider.unset_tags id, parser.tags + end + end + + def prepare_tags 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 + 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) + user = parser.current_user + Devops::Db.connector.check_project_auth s.project, s.deploy_env, 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) diff --git a/devops-service/app/api2/handlers/stack.rb b/devops-service/app/api2/handlers/stack.rb index c13ac7f..0ec83c3 100644 --- a/devops-service/app/api2/handlers/stack.rb +++ b/devops-service/app/api2/handlers/stack.rb @@ -1,4 +1,5 @@ require 'db/mongo/models/stack/stack_factory' +require "app/api2/parsers/stack" require_relative "request_handler" module Devops @@ -6,6 +7,8 @@ module Devops module Handler class Stack < RequestHandler + set_parser Devops::API2_0::Parser::StackParser + def stacks Devops::Db.connector.stacks end @@ -29,6 +32,22 @@ module Devops stack end + def create_stack + object = parser.create + stack_model = Model::StackFactory.create(object['provider'], object) + stack_model.owner = owner_from_request + Devops::Db.connector.stack_insert(stack_model) + + file = JobStarter.start_job(:worker, :stack_bootstrap, + provider: stack.provider, + stack_id: stack.id, + request: @request + ) + puts "Syncing report is located here: #{file}" + + stack + end + def stack id Devops::Db.connector.stack(id) end @@ -57,6 +76,10 @@ module Devops stack = Devops::Db.connector.stack(id) stack.resource(resource_id) end + + def set_run_list id + Devops::Db.connector.set_stack_run_list(id, parser.run_list) + end end end end diff --git a/devops-service/app/api2/handlers/stack_preset.rb b/devops-service/app/api2/handlers/stack_preset.rb index 5d362ee..cd4bb71 100644 --- a/devops-service/app/api2/handlers/stack_preset.rb +++ b/devops-service/app/api2/handlers/stack_preset.rb @@ -2,6 +2,7 @@ require 'json' require 'lib/stack_presets/factory' require 'workers/stack_bootstrap_worker' require 'workers/job_starter' +require "app/api2/parsers/stack_preset" require_relative "request_handler" module Devops @@ -9,6 +10,8 @@ module Devops module Handler class StackPreset < RequestHandler + set_parser Devops::API2_0::Parser::StackPresetParser + def presets Devops::StackPresetsFactory.list end @@ -17,7 +20,8 @@ module Devops Devops::StackPresetsFactory.get(id) end - def apply id, body + def apply id + body = parser.apply preset = Devops::StackPresetsFactory.get(id) stack = preset.create_stack_from_preset(body) stack.owner = owner_from_request diff --git a/devops-service/app/api2/handlers/stack_template.rb b/devops-service/app/api2/handlers/stack_template.rb index 93a0f6a..916ed0c 100644 --- a/devops-service/app/api2/handlers/stack_template.rb +++ b/devops-service/app/api2/handlers/stack_template.rb @@ -1,4 +1,5 @@ require 'db/mongo/models/stack_template/stack_template_factory' +require "app/api2/parsers/stack_template" require_relative "request_handler" module Devops @@ -6,6 +7,8 @@ module Devops module Handler class StackTemplate < RequestHandler + set_parser Devops::API2_0::Parser::StackTemplateParser + def stack_templates Devops::Db.connector.stack_templates end @@ -14,7 +17,8 @@ module Devops Devops::Db.connector.stack_templates(provider) end - def create_stack_template body + def create_stack_template + body = parser.create template_model = Model::StackTemplateFactory.create(body['provider'], body) Devops::Db.connector.stack_template_insert(template_model) template_model diff --git a/devops-service/app/api2/handlers/tag.rb b/devops-service/app/api2/handlers/tag.rb index ea29315..5027ffb 100644 --- a/devops-service/app/api2/handlers/tag.rb +++ b/devops-service/app/api2/handlers/tag.rb @@ -1,9 +1,12 @@ require "commands/knife_commands" +require "app/api2/parsers/tag" +require_relative "request_handler" module Devops module API2_0 module Handler - class Tag + class Tag < RequestHandler + set_parser Devops::API2_0::Parser::TagParser def initialize node_name @node_name = node_name @@ -12,20 +15,24 @@ module Devops halt_response("No servers found for name '#{@node_name}'", 404) if @server.nil? end - def tags - KnifeCommands.tags_list(@node_name) + def tags node_name + KnifeCommands.tags_list(node_name) end - def set_tags tags + def set_tags node_name + tags = parser.tags tagsStr = tags.join(" ") - cmd = KnifeCommands.tags_create(@node_name, tagsStr) - halt_response("Error: Cannot add tags #{tagsStr} to server #{@node_name}", 500) unless cmd[1] + cmd = KnifeCommands.tags_create(node_name, tagsStr) + halt_response("Cannot add tags #{tagsStr} to server #{node_name}", 500) unless cmd[1] + tags end - def self.unset_tags + def unset_tags node_name + tags = parser.tags tagsStr = tags.join(" ") - cmd = KnifeCommands.tags_delete(@node_name, tagsStr) - halt_response("Cannot delete tags #{tagsStr} from server #{@node_name}: #{cmd[0]}", 500) unless cmd[1] + cmd = KnifeCommands.tags_delete(node_name, tagsStr) + halt_response("Cannot delete tags #{tagsStr} from server #{node_name}: #{cmd[0]}", 500) unless cmd[1] + tags end end end diff --git a/devops-service/app/api2/handlers/user.rb b/devops-service/app/api2/handlers/user.rb index f86ee19..5026cfd 100644 --- a/devops-service/app/api2/handlers/user.rb +++ b/devops-service/app/api2/handlers/user.rb @@ -1,39 +1,52 @@ require "db/mongo/models/user" +require "app/api2/parsers/user" +require_relative "request_handler" module Devops module API2_0 module Handler - class User + class User < RequestHandler + + set_parser Devops::API2_0::Parser::UserParser def users - Devops::Db.connector.users + Devops::Db.connector.users({}, fields: {password: false}) end - def create body - Devops::Db.connector.user_insert Devops::Model::User.new(body) + def create + Devops::Db.connector.user_insert parser.create end def delete user_id + projects = Devops::Db.connector.projects_by_user user_id + if !projects.empty? + str = "" + projects.each do |p| + p.deploy_envs.each do |e| + str+="#{p.id}.#{e.identifier} " if e.users.include? user_id + end + end + raise DependencyError.new "Deleting is forbidden: User is included in #{str}" + end Devops::Db.connector.user_delete user_id end - def change_user_privileges user_id, cmd, privileges + def change_user_privileges user_id + cmd, privileges = parser.user_privileges change_user(user_id) do |user| user.grant(cmd, privileges) end end - def change_email options - user_id, email = options.values_at('user_id', 'email') + def change_email user_id change_user(user_id) do |user| - user.email = email + user.email = parser.change_email end end - def change_password options - user_id, password = options.values_at('user_id', 'password') + def change_password user_id change_user(user_id) do |user| - user.password = password + user.password = parser.change_password end end diff --git a/devops-service/app/api2/helpers/parser.rb b/devops-service/app/api2/helpers/parser.rb new file mode 100644 index 0000000..ebf0d5e --- /dev/null +++ b/devops-service/app/api2/helpers/parser.rb @@ -0,0 +1,73 @@ +require "json" +require "exceptions/parser_error" +require "exceptions/validation_error" + +module Devops + module API2_0 + module ParserHelpers + + def create_object_from_json_body type=Hash, empty_body=false + json = request.body.read.strip + return nil if json.empty? and empty_body + @body_json = begin + ::JSON.parse(json) + rescue ::JSON::ParserError => e + DevopsLogger.logger.error e.message + raise Devops::ParserError.new("Invalid JSON: #{e.message}") + end + raise Devops::ParserError.new("Invalid JSON, it should be an #{type == Array ? "array" : "object"}") unless @body_json.is_a?(type) + @body_json + end + + def check_provider provider + list = ::Provider::ProviderFactory.providers + raise Devops::ValidationError.new("Invalid provider '#{provider}', available providers: '#{list.join("', '")}'") unless list.include?(provider) + end + + def check_string val, msg, _nil=false, empty=false + check_param val, String, msg, _nil, empty + end + + def check_array val, msg, vals_type=String, _nil=false, empty=false + check_param val, Array, msg, _nil, empty + val.each {|v| raise Devops::ValidationError.new(msg) unless v.is_a?(vals_type)} unless val.nil? + val + end + + def check_filename file_name, not_string_msg, json_resp=true + check_string file_name, not_string_msg + r = Regexp.new("^[\\w _\\-.]{1,255}$", Regexp::IGNORECASE) + if r.match(file_name).nil? + msg = "Invalid file name '#{file_name}'. Expected name with 'a'-'z', '0'-'9', ' ', '_', '-', '.' symbols with length greate then 0 and less then 256 " + raise Devops::ValidationError.new(msg, json_resp) +=begin + if json_resp + halt_response(msg) + else + halt(400, msg) + end +=end + end + file_name + end + + def check_param val, type, msg, _nil=false, empty=false + if val.nil? + if _nil + return val + else + raise Devops::ValidationError.new(msg) + end + end + if val.is_a?(type) + raise Devops::ValidationError.new(msg) if val.empty? and !empty + val + else + raise Devops::ValidationError.new(msg) + end + end + + end + end +end + diff --git a/devops-service/app/api2/helpers/version_2.rb b/devops-service/app/api2/helpers/version_2.rb index c201032..3e301c9 100644 --- a/devops-service/app/api2/helpers/version_2.rb +++ b/devops-service/app/api2/helpers/version_2.rb @@ -8,6 +8,8 @@ module Devops module API2_0 module Helpers + include Sinatra::JSON + def create_response msg, obj=nil, rstatus=200 logger.info(msg) status rstatus @@ -22,7 +24,6 @@ module Devops end def check_privileges cmd, p - # somewhy REMOTE_USER is missing user = request.env['USER'] user.check_privileges(cmd, p) end @@ -32,59 +33,6 @@ module Devops halt_response("Invalid provider '#{provider}', available providers: '#{list.join("', '")}'", 404) unless list.include?(provider) end - def create_object_from_json_body type=Hash, empty_body=false - json = request.body.read.strip - return nil if json.empty? and empty_body - @body_json = begin - ::JSON.parse(json) - rescue ::JSON::ParserError => e - logger.error e.message - halt_response("Invalid JSON: #{e.message}") - end - halt_response("Invalid JSON, it should be an #{type == Array ? "array" : "object"}") unless @body_json.is_a?(type) - @body_json - end - - def check_string val, msg, _nil=false, empty=false - check_param val, String, msg, _nil, empty - end - - def check_array val, msg, vals_type=String, _nil=false, empty=false - check_param val, Array, msg, _nil, empty - val.each {|v| halt_response(msg) unless v.is_a?(vals_type)} unless val.nil? - val - end - - def check_filename file_name, not_string_msg, json_resp=true - check_string file_name, not_string_msg - r = Regexp.new("^[\\w _\\-.]{1,255}$", Regexp::IGNORECASE) - if r.match(file_name).nil? - msg = "Invalid file name '#{file_name}'. Expected name with 'a'-'z', '0'-'9', ' ', '_', '-', '.' symbols with length greate then 0 and less then 256 " - if json_resp - halt_response(msg) - else - halt(400, msg) - end - end - file_name - end - - def check_param val, type, msg, _nil=false, empty=false - if val.nil? - if _nil - return val - else - halt_response(msg) - end - end - if val.is_a?(type) - halt_response(msg) if val.empty? and !empty - val - else - halt_response(msg) - end - end - # Save information about requests with methods POST, PUT, DELETE def statistic msg=nil unless request.get? diff --git a/devops-service/app/api2/parsers/bootstrap_template.rb b/devops-service/app/api2/parsers/bootstrap_template.rb new file mode 100644 index 0000000..78ef743 --- /dev/null +++ b/devops-service/app/api2/parsers/bootstrap_template.rb @@ -0,0 +1,11 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class BootstrapTemplateParser < RequestParser + + end + end + end +end diff --git a/devops-service/app/api2/parsers/deploy.rb b/devops-service/app/api2/parsers/deploy.rb new file mode 100644 index 0000000..708009a --- /dev/null +++ b/devops-service/app/api2/parsers/deploy.rb @@ -0,0 +1,19 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class DeployParser < RequestParser + + def deploy + r = create_object_from_json_body + names = check_array(r["names"], "Parameter 'names' should be a not empty array of strings") + tags = check_array(r["tags"], "Parameter 'tags' should be an array of strings", String, true) + build_number = check_string(r["build_number"], "Parameter 'build_number' should be a not empty string", true) + r + end + + end + end + end +end diff --git a/devops-service/app/api2/parsers/filter.rb b/devops-service/app/api2/parsers/filter.rb new file mode 100644 index 0000000..5752dab --- /dev/null +++ b/devops-service/app/api2/parsers/filter.rb @@ -0,0 +1,16 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class FilterParser < RequestParser + + def images + images = create_object_from_json_body(Array) + check_array(images, "Request body should contains a not empty array with strings") + end + end + end + end +end + diff --git a/devops-service/app/api2/parsers/image.rb b/devops-service/app/api2/parsers/image.rb new file mode 100644 index 0000000..f801250 --- /dev/null +++ b/devops-service/app/api2/parsers/image.rb @@ -0,0 +1,23 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class ImageParser < RequestParser + + def images + provider = @params[:provider] + check_provider(provider) if provider + provider + end + + def image + image = create_object_from_json_body + Devops::Model::Image.new(image) + end + + end + end + end +end + diff --git a/devops-service/app/api2/parsers/key.rb b/devops-service/app/api2/parsers/key.rb new file mode 100644 index 0000000..3610438 --- /dev/null +++ b/devops-service/app/api2/parsers/key.rb @@ -0,0 +1,19 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class KeyParser < RequestParser + + def create + key = create_object_from_json_body + fname = check_filename(key["file_name"], "Parameter 'file_name' must be a not empty string") + kname = check_string(key["key_name"], "Parameter 'key_name' should be a not empty string") + content = check_string(key["content"], "Parameter 'content' should be a not empty string") + key + end + + end + end + end +end diff --git a/devops-service/app/api2/parsers/project.rb b/devops-service/app/api2/parsers/project.rb new file mode 100644 index 0000000..37c6f59 --- /dev/null +++ b/devops-service/app/api2/parsers/project.rb @@ -0,0 +1,76 @@ +require "app/api2/helpers/version_2" +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class ProjectParser < RequestParser + + include Devops::API2_0::Helpers + + def projects + fields = [] + if @params.key?("fields") and @params["fields"].is_a?(Array) + Devops::Model::Project.fields.each do |k| + fields.push k if @params["fields"].include?(k) + end + end + fields + end + + def archived_projects + @params.include?("archived") + end + + def project_servers + @params[:deploy_env] + end + + def project_stacks + @params[:deploy_env] + end + + def create_project + body = create_object_from_json_body + check_string(body["name"], "Parameter 'name' must be a not empty string") + check_array(body["deploy_envs"], "Parameter 'deploy_envs' must be a not empty array of objects", Hash) + check_array(body["run_list"], "Parameter 'run_list' must be a not empty array of string", String, false, true) + Devops::Model::Project.new(body) + end + + def update + body = create_object_from_json_body + check_array(body["deploy_envs"], "Parameter 'deploy_envs' must be a not empty array of objects", Hash) + check_array(body["run_list"], "Parameter 'run_list' must be a not empty array of string", String, false, true) + Devops::Model::Project.new(body) + end + + def delete + body = create_object_from_json_body(Hash, true) + deploy_env = unless body.nil? + check_string(body["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true) + end + end + + def project_users + body = create_object_from_json_body + users = check_array(body["users"], "Parameter 'users' must be a not empty array of strings") + deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string", true) + return deploy_env, users + end + + def run_list + list = create_object_from_json_body(Array) + check_array(list, "Body must contains not empty array of strings") + end + + def deploy + obj = create_object_from_json_body + deploy_env = check_string(obj["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true) + servers = check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true) + return deploy_env, servers + end + end + end + end +end diff --git a/devops-service/app/api2/parsers/request_parser.rb b/devops-service/app/api2/parsers/request_parser.rb new file mode 100644 index 0000000..621a69f --- /dev/null +++ b/devops-service/app/api2/parsers/request_parser.rb @@ -0,0 +1,30 @@ +require "app/api2/helpers/parser" + +module Devops + module API2_0 + module Parser + class RequestParser + + include Devops::API2_0::ParserHelpers + + def initialize request + @request = request + @params = request.params + end + + def current_user + @request.env['REMOTE_USER'] + end + + def request + @request + end + + def body + @params + end + + end + end + end +end diff --git a/devops-service/app/api2/parsers/script.rb b/devops-service/app/api2/parsers/script.rb new file mode 100644 index 0000000..a495f02 --- /dev/null +++ b/devops-service/app/api2/parsers/script.rb @@ -0,0 +1,26 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class ScriptParser < RequestParser + + def execute_command + @request.body.read + end + + def run_script + body = create_object_from_json_body + nodes = check_array(body["nodes"], "Parameter 'nodes' must be a not empty array of strings") + p = check_array(body["params"], "Parameter 'params' should be a not empty array of strings", String, true) + return nodes, p + end + + def create_script + @request.body.read + end + + end + end + end +end diff --git a/devops-service/app/api2/parsers/server.rb b/devops-service/app/api2/parsers/server.rb new file mode 100644 index 0000000..f1d6f65 --- /dev/null +++ b/devops-service/app/api2/parsers/server.rb @@ -0,0 +1,81 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class ServerParser < RequestParser + + def servers + fields = [] + if @params.key?("fields") and @params["fields"].is_a?(Array) + Devops::Model::Server.fields.each do |k| + fields.push k if @params["fields"].include?(k) + end + end + return fields, (@params.key?("reserved") ? true : nil) + end + + def server + @params[:key] + end + + def instance_key + @body ||= create_object_from_json_body(Hash, true) + (@body.nil? ? nil : @body["key"]) + end + + def create + @body ||= create_object_from_json_body + project_name = check_string(@body["project"], "Parameter 'project' must be a not empty string") + env_name = check_string(@body["deploy_env"], "Parameter 'deploy_env' must be a not empty string") + server_name = check_string(@body["name"], "Parameter 'name' should be null or not empty string", true) + without_bootstrap = @body["without_bootstrap"] + force = @body["force"] + raise InvalidRecord.new("Parameter 'without_bootstrap' should be a null or true") unless without_bootstrap.nil? or without_bootstrap == true + raise InvalidRecord.new("Parameter 'force' should be a null or true") unless force.nil? or force == true + groups = check_array(@body["groups"], "Parameter 'groups' should be null or not empty array of string", String, true) + key_name = check_string(@body["key"], "Parameter 'key' should be null or not empty string", true) + rl = check_array(@body["run_list"], "Parameter 'run_list' should be a not empty array of string", String, true, true) + Validators::Helpers::RunList.new(rl).validate! unless rl.nil? + @body + end + + def bootstrap + @body ||= create_object_from_json_body(Hash, true) + id = check_string(@body["instance_id"], "Parameter 'instance_id' must be a not empty string") + name = check_string(@body["name"], "Parameter 'name' should be a not empty string", true) + rl = check_array(@body["run_list"], "Parameter 'run_list' should be a not empty array of string", String, true) + Validators::Helpers::RunList.new(rl).validate! unless rl.nil? + t = check_string(@body["bootstrap_template"], "Parameter 'bootstrap_template' should be a not empty string", true) + @body + end + + def add_server + @body ||= create_object_from_json_body + project = check_string(@body["project"], "Parameter 'project' must be a not empty string") + deploy_env = check_string(@body["deploy_env"], "Parameter 'deploy_env' must be a not empty string") + key = check_string(@body["key"], "Parameter 'key' must be a not empty string") + remote_user = check_string(@body["remote_user"], "Parameter 'remote_user' must be a not empty string") + private_ip = check_string(@body["private_ip"], "Parameter 'private_ip' must be a not empty string") + public_ip = check_string(@body["public_ip"], "Parameter 'public_ip' should be a not empty string", true) + rl = check_array(@body["run_list"], "Parameter 'run_list' should be a not empty array of string", String, true, true) + Validators::Helpers::RunList.new(rl).validate! unless rl.nil? + @body + end + + def tags + @body ||= create_object_from_json_body + check_param(@body["tags"], Hash, "Parameter 'tags' should be a hash", false, true) + end + + def run_list + @body ||= create_object_from_json_body + rl = check_array(@body["run_list"], "Parameter 'run_list' should be a not empty array of string", String, false, true) + Validators::Helpers::RunList.new(rl).validate! + rl + end + + end + end + end +end diff --git a/devops-service/app/api2/parsers/stack.rb b/devops-service/app/api2/parsers/stack.rb new file mode 100644 index 0000000..ebb8f15 --- /dev/null +++ b/devops-service/app/api2/parsers/stack.rb @@ -0,0 +1,24 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class StackParser < RequestParser + + def create + body = create_object_from_json_body + list = check_array(body["run_list"], "Body must contains not empty array of strings", String, true, true) + Validators::Helpers::RunList.new(list).validate! unless list.nil? + body + end + + def run_list + list = check_array(create_object_from_json_body(Array), "Body must contains not empty array of strings") + Validators::Helpers::RunList.new(list).validate! + list + end + + end + end + end +end diff --git a/devops-service/app/api2/parsers/stack_preset.rb b/devops-service/app/api2/parsers/stack_preset.rb new file mode 100644 index 0000000..6c921ff --- /dev/null +++ b/devops-service/app/api2/parsers/stack_preset.rb @@ -0,0 +1,16 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class StackPresetParser < RequestParser + + def apply + create_object_from_json_body + end + + end + end + end +end + diff --git a/devops-service/app/api2/parsers/stack_template.rb b/devops-service/app/api2/parsers/stack_template.rb new file mode 100644 index 0000000..3817adf --- /dev/null +++ b/devops-service/app/api2/parsers/stack_template.rb @@ -0,0 +1,15 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class StackTemplateParser < RequestParser + + def create + create_object_from_json_body + end + + end + end + end +end diff --git a/devops-service/app/api2/parsers/tag.rb b/devops-service/app/api2/parsers/tag.rb new file mode 100644 index 0000000..2ed6d03 --- /dev/null +++ b/devops-service/app/api2/parsers/tag.rb @@ -0,0 +1,17 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class TagParser < RequestParser + + def tags + tags = create_object_from_json_body(Array) + check_array(tags, "Request body should be a not empty array of strings") + end + + end + end + end +end + diff --git a/devops-service/app/api2/parsers/user.rb b/devops-service/app/api2/parsers/user.rb new file mode 100644 index 0000000..33266fb --- /dev/null +++ b/devops-service/app/api2/parsers/user.rb @@ -0,0 +1,39 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class UserParser < RequestParser + + def create + user = create_object_from_json_body + ["username", "password", "email"].each do |p| + check_string(user[p], "Parameter '#{p}' must be a not empty string") + end + Devops::Model::User.new(user) + end + + def user_privileges + data = create_object_from_json_body + cmd = check_string(data["cmd"], "Parameter 'cmd' should be a not empty string", true) + privileges = check_string(data["privileges"], "Parameter 'privileges' should be a not empty string", true) + return cmd, privileges + end + + def change_password + raise InvalidPrivileges.new("Access denied for '#{current_user}'") if user == Devops::Model::User::ROOT_USER_NAME and current_user != Devops::Model::User::ROOT_USER_NAME + body = create_object_from_json_body + check_string(body["password"], "Parameter 'password' must be a not empty string") + end + + def change_email + raise InvalidPrivileges.new("Access denied for '#{current_user}'") if user == Devops::Model::User::ROOT_USER_NAME and current_user != Devops::Model::User::ROOT_USER_NAME + body = create_object_from_json_body + check_string(body["email"], "Parameter 'email' must be a not empty string") + end + + end + end + end +end + diff --git a/devops-service/app/api2/routes/bootstrap_templates.rb b/devops-service/app/api2/routes/bootstrap_templates.rb index 93395e4..60d27a8 100644 --- a/devops-service/app/api2/routes/bootstrap_templates.rb +++ b/devops-service/app/api2/routes/bootstrap_templates.rb @@ -3,8 +3,6 @@ module Devops module Routes module BootstrapTemplatesRoutes - extend BootstrapTemplatesCommands - def self.registered(app) # Get list of available bootstrap templates @@ -20,7 +18,7 @@ module Devops # ] app.get_with_headers "/templates", :headers => [:accept] do check_privileges("templates", "r") - json Devops::API2_0::Handler::BootstrapTemplates.new.get_templates + json Devops::API2_0::Handler::BootstrapTemplates.new(request).get_templates end puts "Bootstrap templates routes initialized" diff --git a/devops-service/app/api2/routes/deploy.rb b/devops-service/app/api2/routes/deploy.rb index 045ec9f..496bbf8 100644 --- a/devops-service/app/api2/routes/deploy.rb +++ b/devops-service/app/api2/routes/deploy.rb @@ -16,33 +16,28 @@ module Devops # { # "names": [], -> array of servers names to run chef-client # "tags": [], -> array of tags to apply on each server before running chef-client - # "trace": true -> return output in stream + # "build_number": "" -> string, build number to deploy # } # # * *Returns* : text stream app.post_with_headers "/deploy", :headers => [:content_type] do check_privileges("server", "x") - # TODO: send message - #broadcast(:devops_deploy, "deploy") - r = create_object_from_json_body - names = check_array(r["names"], "Parameter 'names' should be a not empty array of strings") - tags = check_array(r["tags"], "Parameter 'tags' should be an array of strings", String, true) || [] - if r.key?("trace") + if request["X-Stream"] stream() do |out| status = [] begin - Devops::API2_0::Handler::Deploy.new(request, params).deploy_stream(out, names, tags) + status = Devops::API2_0::Handler::Deploy.new(request).deploy_stream(out) + out << create_status(status) rescue IOError => e logger.error e.message break end end # stream else - ids = Devops::API2_0::Handler::Deploy.new(request, params).deploy(names, tags) + ids = Devops::API2_0::Handler::Deploy.new(request).deploy() sleep 1 ids.each do |jid| - logger.info "Job '#{jid}' has been queued" uri.path = "#{DevopsConfig.config[:url_prefix]}/v2.0/report/" + jid files.push uri.to_s end diff --git a/devops-service/app/api2/routes/filter.rb b/devops-service/app/api2/routes/filter.rb index b443ea8..e3efe85 100644 --- a/devops-service/app/api2/routes/filter.rb +++ b/devops-service/app/api2/routes/filter.rb @@ -26,7 +26,7 @@ module Devops app.get_with_headers "/filter/:provider/images", :headers => [:accept] do |provider| check_privileges("filter", "r") check_provider(provider) - json Devops::API2_0::Handler::Filter.new(request, params).available_images(provider) + json Devops::API2_0::Handler::Filter.new(request).available_images(provider) end hash = {} @@ -47,10 +47,7 @@ module Devops hash["PUT"] = lambda { |provider| check_privileges("filter", "w") check_provider(provider) - images = create_object_from_json_body(Array) - halt_response("Request body should not be an empty array") if images.empty? - check_array(images, "Request body should contains an array with strings") - create_response("Updated", {:images => Devops::API2_0::Handler::Filter.new(request, params).add_images(images, provider)}) + create_response("Updated", {:images => Devops::API2_0::Handler::Filter.new(request).add_images(provider)}) } # Delete image ids from filter for :provider @@ -69,10 +66,7 @@ module Devops hash["DELETE"] = lambda { |provider| check_privileges("filter", "w") check_provider(provider) - images = create_object_from_json_body(Array) - halt_response("Request body should not be an empty array") if images.empty? - check_array(images, "Request body should contains an array with strings") - create_response("Deleted", {:images => Devops::API2_0::Handler::Filter.new(request, params).delete_images(images, provider)}) + create_response("Deleted", {:images => Devops::API2_0::Handler::Filter.new(request).delete_images(provider)}) } app.multi_routes "/filter/:provider/image", {:headers => [:accept, :content_type]}, hash diff --git a/devops-service/app/api2/routes/flavor.rb b/devops-service/app/api2/routes/flavor.rb index 68a0559..d0e5ea4 100644 --- a/devops-service/app/api2/routes/flavor.rb +++ b/devops-service/app/api2/routes/flavor.rb @@ -31,11 +31,10 @@ module Devops # "disk": 20 # } # ] - app.get_with_headers "/flavors/:provider", :headers => [:accept] do + app.get_with_headers "/flavors/:provider", :headers => [:accept] do |provider| check_privileges("flavor", "r") - provider = params[:provider] check_provider(provider) - json Devops::API2_0::Handler::Flavor.new(request, params).flavors + json Devops::API2_0::Handler::Flavor.new(request).flavors(provider) end puts "Flavor routes initialized" diff --git a/devops-service/app/api2/routes/group.rb b/devops-service/app/api2/routes/group.rb index ed8238e..6ba87a7 100644 --- a/devops-service/app/api2/routes/group.rb +++ b/devops-service/app/api2/routes/group.rb @@ -44,11 +44,10 @@ module Devops # } # } # TODO: vpc support for ec2 - app.get_with_headers "/groups/:provider", :headers => [:accept] do#, &Devops::Version2_0::Handler::Group.get_groups + app.get_with_headers "/groups/:provider", :headers => [:accept] do |provider| check_privileges("group", "r") - provider = params[:provider] check_provider(provider) - json Devops::API2_0::Handler::Group.new(provider).groups(params) + json Devops::API2_0::Handler::Group.new(request).groups(provider) end puts "Group routes initialized" diff --git a/devops-service/app/api2/routes/image.rb b/devops-service/app/api2/routes/image.rb index b30b861..d6eedfc 100644 --- a/devops-service/app/api2/routes/image.rb +++ b/devops-service/app/api2/routes/image.rb @@ -26,9 +26,7 @@ module Devops # ] app.get_with_headers "/images", :headers => [:accept] do check_privileges("image", "r") - provider = params[:provider] - check_provider(provider) if provider - json Devops::API2_0::Handler::Image.new(request, params).images(provider).map(&:to_hash) + json Devops::API2_0::Handler::Image.new(request).images.map(&:to_hash) end # Get raw images for :provider @@ -58,7 +56,7 @@ module Devops app.get_with_headers "/images/provider/:provider", :headers => [:accept] do |provider| check_privileges("image", "r") check_provider(provider) - json Devops::API2_0::Handler::Image.new(request, params).provider_images(provider) + json Devops::API2_0::Handler::Image.new(request).provider_images(provider) end # Create devops image @@ -81,8 +79,7 @@ module Devops # 201 - Created app.post_with_headers "/image", :headers => [:accept, :content_type] do check_privileges("image", "w") - image = create_object_from_json_body - Devops::API2_0::Handler::Image.new(request, params).create_image(image) + Devops::API2_0::Handler::Image.new(request).create_image() create_response "Created", nil, 201 end @@ -104,7 +101,7 @@ module Devops # } hash["GET"] = lambda { |image_id| check_privileges("image", "r") - json Devops::API2_0::Handler::Image.new(request, params).image(image_id) + json Devops::API2_0::Handler::Image.new(request).image(image_id) } # Update devops image @@ -127,8 +124,7 @@ module Devops # 200 - Updated hash["PUT"] = lambda {|image_id| check_privileges("image", "w") - image = create_object_from_json_body - Devops::API2_0::Handler::Image.new(request, params).update_image(image_id, image) + Devops::API2_0::Handler::Image.new(request).update_image(image_id) create_response("Image '#{image_id}' has been updated") } @@ -143,7 +139,7 @@ module Devops # 200 - Deleted hash["DELETE"] = lambda {|image_id| check_privileges("image", "w") - Devops::API2_0::Handler::Image.new(request, params).delete_image(image_id) + Devops::API2_0::Handler::Image.new(request).delete_image(image_id) create_response("Image '#{image_id}' has been removed") } diff --git a/devops-service/app/api2/routes/key.rb b/devops-service/app/api2/routes/key.rb index b7c89fc..a4d5a64 100644 --- a/devops-service/app/api2/routes/key.rb +++ b/devops-service/app/api2/routes/key.rb @@ -22,7 +22,7 @@ module Devops # ] app.get_with_headers "/keys", :headers => [:accept] do check_privileges("key", "r") - json Devops::API2_0::Handler::Key.new.keys + json Devops::API2_0::Handler::Key.new(request).keys.map(&:to_hash) end # Create ssh key on devops server @@ -43,13 +43,7 @@ module Devops # 201 - Created app.post_with_headers "/key", :headers => [:accept, :content_type] do check_privileges("key", "w") - key = create_object_from_json_body - fname = check_filename(key["file_name"], "Parameter 'file_name' must be a not empty string") - kname = check_string(key["key_name"], "Parameter 'key_name' should be a not empty string") - content = check_string(key["content"], "Parameter 'content' should be a not empty string") - file_name = File.join(settings.keys_dir, fname) - halt(400, "File '#{fname}' already exist") if File.exists?(file_name) - Devops::API2_0::Handler::Key.new.create(key, file_name) + Devops::API2_0::Handler::Key.new(request).create create_response("Created", nil, 201) end @@ -62,13 +56,12 @@ module Devops # # * *Returns* : # 200 - Deleted - app.delete_with_headers "/key/:key", :headers => [:accept] do + app.delete_with_headers "/key/:key", :headers => [:accept] do |key| check_privileges("key", "w") - r = Devops::API2_0::Handler::Key.new.delete params[:key] + r = Devops::API2_0::Handler::Key.new(request).delete key return [500, r["err"].inspect] if r["err"] - create_response("Key '#{params[:key]}' removed") - + create_response("Key '#{key}' removed") end puts "Key routes initialized" diff --git a/devops-service/app/api2/routes/network.rb b/devops-service/app/api2/routes/network.rb index 2d6c738..3d31d64 100644 --- a/devops-service/app/api2/routes/network.rb +++ b/devops-service/app/api2/routes/network.rb @@ -35,7 +35,7 @@ module Devops app.get_with_headers "/networks/:provider", :headers => [:accept] do |provider| check_privileges("network", "r") check_provider(provider) - json Devops::API2_0::Handler::Network.new(request, params).networks(provider) + json Devops::API2_0::Handler::Network.new(request).networks(provider) end puts "Network routes initialized" diff --git a/devops-service/app/api2/routes/project.rb b/devops-service/app/api2/routes/project.rb index 6dc90e4..ee4bee1 100644 --- a/devops-service/app/api2/routes/project.rb +++ b/devops-service/app/api2/routes/project.rb @@ -5,6 +5,22 @@ module Devops def self.registered(app) + # Get project types + # + # * *Request* + # - method : GET + # - headers : + # - Accept: application/json + # + # * *Returns* : + # [ + # "type" + # ] + app.get_with_headers "/project_types", :headers => [:accept] do + check_privileges("project", "r") + json Devops::API2_0::Handler::Project.new(request).project_types + end + # Get projects list # # * *Request* @@ -20,7 +36,7 @@ module Devops # ] app.get_with_headers "/projects", :headers => [:accept] do check_privileges("project", "r") - json Devops::API2_0::Handler::Project.new(request, params).projects.map(&:to_hash) + json Devops::API2_0::Handler::Project.new(request).projects.map(&:to_hash) end # Get project by id @@ -58,7 +74,7 @@ module Devops hash = {} hash["GET"] = lambda {|project| check_privileges("project", "r") - json Devops::API2_0::Handler::Project.new(request, params).project(project) + json Devops::API2_0::Handler::Project.new(request).project(project) } # Update project and create chef roles @@ -98,8 +114,7 @@ module Devops # 200 - Updated hash["PUT"] = lambda { |project| check_privileges("project", "w") - body = create_object_from_json_body - r = Devops::API2_0::Handler::Project.new(request, params).update_project project, body + r = Devops::API2_0::Handler::Project.new(request).update_project project info = "Project '#{project}' has been updated." + r create_response(info) } @@ -120,11 +135,7 @@ module Devops # 200 - Deleted hash["DELETE"] = lambda {|project| check_privileges("project", "w") - body = create_object_from_json_body(Hash, true) - deploy_env = unless body.nil? - check_string(body["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true) - end - info = Devops::API2_0::Handler::Project.new(request, params).delete_project(project, deploy_env) + info = Devops::API2_0::Handler::Project.new(request).delete_project(project) create_response(info) } app.multi_routes "/project/:project", {}, hash @@ -157,7 +168,7 @@ module Devops # ] app.get_with_headers "/project/:project/servers", :headers => [:accept] do |project| check_privileges("project", "r") - json Devops::API2_0::Handler::Project.new(request, params).project_servers(project).map(&:to_hash) + json Devops::API2_0::Handler::Project.new(request).project_servers(project).map(&:to_hash) end # Get project stacks @@ -187,7 +198,7 @@ module Devops # ] app.get_with_headers "/project/:project/stacks", :headers => [:accept] do |project| check_privileges("project", "r") - json Devops::API2_0::Handler::Project.new(request, params).project_stacks(project).map(&:to_hash) + json Devops::API2_0::Handler::Project.new(request).project_stacks(project).map(&:to_hash) end # Create project and chef roles @@ -227,11 +238,8 @@ module Devops # 201 - Created app.post_with_headers "/project", :headers => [:accept, :content_type] do check_privileges("project", "w") - body = create_object_from_json_body - check_string(body["name"], "Parameter 'name' must be a not empty string") - check_array(body["deploy_envs"], "Parameter 'deploy_envs' must be a not empty array of objects", Hash) begin - res = Devops::API2_0::Handler::Project.new(request, params).create_project body + res = Devops::API2_0::Handler::Project.new(request).create_project res = "Created. " + res create_response(res, nil, 201) rescue InvalidRecord => e @@ -259,10 +267,7 @@ module Devops # 200 - Updated users_hash["PUT"] = lambda { |project| check_privileges("project", "w") - body = create_object_from_json_body - users = check_array(body["users"], "Parameter 'users' must be a not empty array of strings") - deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string", true) - info = Devops::API2_0::Handler::Project.new(request, params).update_project_users(project, deploy_env, users) + info = Devops::API2_0::Handler::Project.new(request).update_project_users(project) create_response(info) } @@ -285,18 +290,15 @@ module Devops # 200 - Updated users_hash["DELETE"] = lambda {|project| check_privileges("project", "w") - body = create_object_from_json_body - users = check_array(body["users"], "Parameter 'users' must be a not empty array of strings") - deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string", true) - info = Devops::API2_0::Handler::Project.new(request, params).delete_project_users(project, deploy_env, users) + info = Devops::API2_0::Handler::Project.new(request).delete_project_users(project) create_response(info) } app.multi_routes "/project/:id/user", {}, users_hash - # Set run_list to project environment + # Set run_list to project # # * *Request* - # - method : PUT + # - method : PATCH # - headers : # - Accept: application/json # - Content-Type: application/json @@ -308,11 +310,30 @@ module Devops # # * *Returns* : # 200 - Updated - app.put_with_headers "/project/:id/:env/run_list", :headers => [:accept, :content_type] do |project, deploy_env| + app.patch_with_headers "/project/:id/run_list", :headers => [:accept, :content_type] do |project| check_privileges("project", "w") - list = create_object_from_json_body(Array) - check_array(list, "Body must contains not empty array of strings") - info = Devops::API2_0::Handler::Project.new(request, params).set_project_env_run_list(project, deploy_env, list) + info = Devops::API2_0::Handler::Project.new(request).set_project_run_list(project) + create_response(info) + end + + # Set run_list to project environment + # + # * *Request* + # - method : PATCH + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # [ + # "role[role_1]", + # "recipe[recipe_1]" + # ] + # + # * *Returns* : + # 200 - Updated + app.patch_with_headers "/project/:id/:env/run_list", :headers => [:accept, :content_type] do |project, deploy_env| + check_privileges("project", "w") + info = Devops::API2_0::Handler::Project.new(request).set_project_env_run_list(project, deploy_env) create_response(info) end @@ -321,6 +342,7 @@ module Devops # * *Request* # - method : POST # - headers : + # - X-Stream: true -> return output in text stream # - Content-Type: application/json # - body : # { @@ -333,21 +355,18 @@ module Devops # * *Returns* : text stream app.post_with_headers "/project/:id/deploy", :headers => [:content_type] do |project| check_privileges("project", "x") - obj = create_object_from_json_body - deploy_env = check_string(obj["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true) - servers = check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true) - handler = Devops::API2_0::Handler::Project.new(request, params) - if obj.key?("trace") + handler = Devops::API2_0::Handler::Project.new(request) + if request["X-Stream"] stream() do |out| begin - status = handler.deploy_project_stream out, project, deploy_env, servers + status = handler.deploy_project_stream out, project out << create_status(status) rescue IOError => e logger.error e.message end end else - json handler.deploy_project project, deploy_env, servers + json handler.deploy_project project end end @@ -359,7 +378,7 @@ module Devops # - Content-Type: application/json app.post_with_headers "/project/:project/archive", :headers => [:content_type] do |project| check_privileges("project", "w") - info = Devops::API2_0::Handler::Project.new(request, params).archive_project(project) + info = Devops::API2_0::Handler::Project.new(request).archive_project(project) create_response(info) end @@ -371,7 +390,7 @@ module Devops # - Content-Type: application/json app.post_with_headers "/project/:project/unarchive", :headers => [:content_type] do |project| check_privileges("project", "w") - info = Devops::API2_0::Handler::Project.new(request, params).unarchive_project(project) + info = Devops::API2_0::Handler::Project.new(request).unarchive_project(project) create_response(info) end @@ -443,7 +462,7 @@ module Devops # } app.post_with_headers "/project/test/:id/:env", :headers => [:accept, :content_type] do |project, deploy_env| check_privileges("project", "r") - json Devops::API2_0::Handler::Project.new(request, params).test_project(project, deploy_env) + json Devops::API2_0::Handler::Project.new(request).test_project(project, deploy_env) end puts "Project routes initialized" diff --git a/devops-service/app/api2/routes/provider.rb b/devops-service/app/api2/routes/provider.rb index 400a6d4..0c1f627 100644 --- a/devops-service/app/api2/routes/provider.rb +++ b/devops-service/app/api2/routes/provider.rb @@ -23,7 +23,7 @@ module Devops # ] app.get_with_headers "/providers", :headers => [:accept] do#, &Devops::Version2_0::Handler::Provider.get_providers check_privileges("provider", "r") - json Devops::API2_0::Handler::Provider.new(request, params).providers + json Devops::API2_0::Handler::Provider.new(request).providers end puts "Provider routes initialized" diff --git a/devops-service/app/api2/routes/report.rb b/devops-service/app/api2/routes/report.rb index 48cdfa0..461c36f 100644 --- a/devops-service/app/api2/routes/report.rb +++ b/devops-service/app/api2/routes/report.rb @@ -6,24 +6,24 @@ module Devops def self.registered(app) app.get_with_headers "/report/all", headers: [:accept] do - json Devops::API2_0::Handler::Report.new(request, params).all.map{|r| r.to_hash} + json Devops::API2_0::Handler::Report.new(request).all.map{|r| r.to_hash} end app.get_with_headers "/report/all/latest", headers: [:accept] do - json Devops::API2_0::Handler::Report.new(request, params).all_latest.map{|r| r.to_hash} + json Devops::API2_0::Handler::Report.new(request).all_latest.map{|r| r.to_hash} end app.get_with_headers "/report/all/attributes/:name", headers: [:accept] do |name| - json Devops::API2_0::Handler::Report.new(request, params).attributes(name) + json Devops::API2_0::Handler::Report.new(request).attributes(name) end app.get_with_headers "/report/:id", headers: [:accept] do |id| - @text, @done = Devops::API2_0::Handler::Report.new(request, params).report(id) - erb :index + @text, @done = Devops::API2_0::Handler::Report.new(request).report(id) + erb :report end - app.get "/status/:id" do - r = Devops::API2_0::Handler::Report.new(request, params).status(params[:id]) + app.get "/status/:id" do |id| + r = Devops::API2_0::Handler::Report.new(request).status(id) return [404, "Job with id '#{params[:id]}' not found"] if r.nil? r end @@ -36,26 +36,3 @@ module Devops end end -__END__ - -@@ layout - -
- <% unless @done %> - - <% end %> - - - <%= yield %> - - - -@@ index --<%= @text %> -diff --git a/devops-service/app/api2/routes/script.rb b/devops-service/app/api2/routes/script.rb index e038781..013a1fd 100644 --- a/devops-service/app/api2/routes/script.rb +++ b/devops-service/app/api2/routes/script.rb @@ -17,7 +17,7 @@ module Devops # ] app.get_with_headers "/scripts", :headers => [:accept] do check_privileges("script", "r") - json Devops::API2_0::Handler::Script.new(request, params).scripts + json Devops::API2_0::Handler::Script.new(request).scripts end # Run command on node :node_name @@ -32,7 +32,7 @@ module Devops check_privileges("script", "x") stream() do |out| begin - Devops::API2_0::Handler::Script.new(request, params).execute_command(out, request.body.read, node_name) + Devops::API2_0::Handler::Script.new(request).execute_command(out, node_name) rescue IOError => e logger.error e.message end @@ -54,14 +54,10 @@ module Devops # * *Returns* : text stream app.post_with_headers "/script/run/:script_name", :headers => [:content_type] do |script_name| check_privileges("script", "x") - file_name = check_filename(script_name, "Parameter 'script_name' must be a not empty string", false) - body = create_object_from_json_body - nodes = check_array(body["nodes"], "Parameter 'nodes' must be a not empty array of strings") - p = check_array(body["params"], "Parameter 'params' should be a not empty array of strings", String, true) - stream() do |out| begin - Devops::API2_0::Handler::Script.new(request, params).run_script out, file_name, nodes, p + status = Devops::API2_0::Handler::Script.new(request).run_script out, script_name + out << create_status(status) rescue IOError => e logger.error e.message end @@ -79,12 +75,10 @@ module Devops # # * *Returns* : # 201 - Created - hash["PUT"] = lambda { + hash["PUT"] = lambda { |script_name| check_privileges("script", "w") - file_name = params[:script_name] - check_filename(file_name, "Parameter 'script_name' must be a not empty string") - Devops::API2_0::Handler::Script.new(request, params).create_script(file_name) - create_response("File '#{file_name}' created", nil, 201) + Devops::API2_0::Handler::Script.new(request).create_script(script_name) + create_response("File '#{script_name}' created", nil, 201) } # Delete script :script_name @@ -96,12 +90,10 @@ module Devops # # * *Returns* : # 200 - Deleted - hash["DELETE"] = lambda { + hash["DELETE"] = lambda { |script_name| check_privileges("script", "w") - file_name = params[:script_name] - check_filename(file_name, "Parameter 'script_name' must be a not empty string") - Devops::API2_0::Handler::Script.new(request, params).delete_script file_name - create_response("File '#{file_name}' deleted") + Devops::API2_0::Handler::Script.new(request).delete_script script_name + create_response("File '#{script_name}' deleted") } app.multi_routes "/script/:script_name", {:headers => [:accept]}, hash diff --git a/devops-service/app/api2/routes/server.rb b/devops-service/app/api2/routes/server.rb index b82f35a..f7c10af 100644 --- a/devops-service/app/api2/routes/server.rb +++ b/devops-service/app/api2/routes/server.rb @@ -1,5 +1,3 @@ -require "json" - module Devops module API2_0 module Routes @@ -25,14 +23,7 @@ module Devops # ] app.get_with_headers "/servers", :headers => [:accept] do check_privileges("server", "r") - fields = [] - if params.key?("fields") and params["fields"].is_a?(Array) - Devops::Model::Server.fields.each do |k| - fields.push k if params["fields"].include?(k) - end - end - reserved = (params.key?("reserved") ? true : nil) - json Devops::API2_0::Handler::Server.new(request, params).servers(fields, reserved) + json Devops::API2_0::Handler::Server.new(request).servers end # Get chef nodes list @@ -50,7 +41,7 @@ module Devops # ] app.get_with_headers "/servers/chef", :headers => [:accept] do check_privileges("server", "r") - json Devops::API2_0::Handler::Server.new(request, params).chef_servers + json Devops::API2_0::Handler::Server.new(request).chef_servers end # Get provider servers list @@ -89,9 +80,9 @@ module Devops # "private_ip": "172.17.0.1" # } # ] - app.get_with_headers "/servers/:provider", :headers => [:accept] do + app.get_with_headers "/servers/:provider", :headers => [:accept] do |provider| check_privileges("server", "r") - json Devops::API2_0::Handler::Server.new(request, params).provider_servers(params[:provider]) + json Devops::API2_0::Handler::Server.new(request).provider_servers(provider) end # Get server info by :name @@ -112,7 +103,7 @@ module Devops hash = {} hash["GET"] = lambda {|id| check_privileges("server", "r") - json Devops::API2_0::Handler::Server.new(request, params).server(id).to_hash + json Devops::API2_0::Handler::Server.new(request).server(id).to_hash } # Delete devops server @@ -131,9 +122,7 @@ module Devops # 200 - Deleted hash["DELETE"] = lambda {|id| check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - info, r = Devops::API2_0::Handler::Server.new(request, params).delete(id, key) + info, r = Devops::API2_0::Handler::Server.new(request).delete(id) create_response(info, r) } app.multi_routes "/server/:id", {:headers => [:accept, :content_type]}, hash @@ -143,6 +132,7 @@ module Devops # * *Request* # - method : POST # - headers : + # - X-Stream: true -> return output in text stream # - Accept: application/json # - Content-Type: application/json # - body : @@ -153,35 +143,24 @@ module Devops # "without_bootstrap": null, -> do not install chef on instance if true # "force": null, -> do not delete server on error # "groups": [], -> specify special security groups, overrides value from project env - # "key": "ssh key", -> specify ssh key for server, overrides value from project env - # "trace": true -> return output in stream + # "key": "ssh key" -> specify ssh key for server, overrides value from project env # } # # * *Returns* : text stream - app.post_with_headers "/server", :headers => [:content_type] do + app.post_with_headers "/server", :headers => [:accept, :content_type] do check_privileges("server", "w") - body = create_object_from_json_body - project_name = check_string(body["project"], "Parameter 'project' must be a not empty string") - env_name = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string") - server_name = check_string(body["name"], "Parameter 'name' should be null or not empty string", true) - without_bootstrap = body["without_bootstrap"] - force = body["force"] - halt_response("Parameter 'without_bootstrap' should be a null or true") unless without_bootstrap.nil? or without_bootstrap == true - halt_response("Parameter 'force' should be a null or true") unless force.nil? or force == true - groups = check_array(body["groups"], "Parameter 'groups' should be null or not empty array of string", String, true) - key_name = check_string(body["key"], "Parameter 'key' should be null or not empty string", true) - handler = Devops::API2_0::Handler::Server.new(request, params) - if body.key?("trace") + handler = Devops::API2_0::Handler::Server.new(request) + if request["X-Stream"] stream() do |out| begin - status = handler.create_server_stream out, body + status = handler.create_server_stream out out << create_status(status) rescue IOError => e logger.error e.message end end else - json handler.create_server body + json handler.create_server end end @@ -201,9 +180,7 @@ module Devops # 200 - Paused app.post_with_headers "/server/:node_name/pause", :headers => [:accept, :content_type] do |node_name| check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - info = Devops::API2_0::Handler::Server.new(request, params).pause_server(node_name, key) + info = Devops::API2_0::Handler::Server.new(request).pause_server(node_name) create_response(info) end @@ -223,9 +200,7 @@ module Devops # 200 - Unpaused app.post_with_headers "/server/:node_name/unpause", :headers => [:accept, :content_type] do |node_name| check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - info = Devops::API2_0::Handler::Server.new(request, params).unpause_server(node_name, key) + info = Devops::API2_0::Handler::Server.new(request).unpause_server(node_name) create_response(info) end @@ -245,9 +220,7 @@ module Devops # 200 - Reserved app.post_with_headers "/server/:node_name/reserve", :headers => [:accept, :content_type] do |node_name| check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - Devops::API2_0::Handler::Server.new(request, params).reserve_server(node_name, key) + Devops::API2_0::Handler::Server.new(request).reserve_server(node_name) create_response("Server '#{node_name}' has been reserved") end @@ -267,9 +240,7 @@ module Devops # 200 - Unreserved app.post_with_headers "/server/:node_name/unreserve", :headers => [:accept, :content_type] do |node_name| check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - Devops::API2_0::Handler::Server.new(request, params).unreserve_server(node_name, key) + Devops::API2_0::Handler::Server.new(request).unreserve_server(node_name) create_response("Server '#{node_name}' has been unreserved") end @@ -278,6 +249,7 @@ module Devops # * *Request* # - method : POST # - headers : + # - X-Stream: true -> return output in text stream # - Accept: application/json # - Content-Type: application/json # - body : @@ -291,28 +263,19 @@ module Devops # * *Returns* : text stream app.post_with_headers "/server/bootstrap", :headers => [:accept, :content_type] do check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - id = check_string(body["instance_id"], "Parameter 'instance_id' must be a not empty string") - name = check_string(body["name"], "Parameter 'name' should be a not empty string", true) - rl = check_array(body["run_list"], "Parameter 'run_list' should be a not empty array of string", String, true) - unless rl.nil? - validator = Validators::Helpers::RunList.new(rl) - halt_response(validator.message) unless validator.valid? - end - t = check_string(body["bootstrap_template"], "Parameter 'bootstrap_template' should be a not empty string", true) - handler = Devops::API2_0::Handler::Server.new(request, params) - if body.key?("trace") + handler = Devops::API2_0::Handler::Server.new(request) + if request["X-Stream"] stream() do |out| begin - status = handler.bootstrap_server_stream out, body + status = handler.bootstrap_server_stream out out << create_status(status) rescue IOError => e logger.error e.message end end else - handler.bootstrap_server(body) + json handler.bootstrap_server() end end @@ -337,18 +300,76 @@ module Devops # 200 - Added app.post_with_headers "/server/add", :headers => [:accept, :content_type] do check_privileges("server", "w") - body = create_object_from_json_body - project = check_string(body["project"], "Parameter 'project' must be a not empty string") - deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string") - key = check_string(body["key"], "Parameter 'key' must be a not empty string") - remote_user = check_string(body["remote_user"], "Parameter 'remote_user' must be a not empty string") - private_ip = check_string(body["private_ip"], "Parameter 'private_ip' must be a not empty string") - public_ip = check_string(body["public_ip"], "Parameter 'public_ip' should be a not empty string", true) - - info = Devops::API2_0::Handler::Server.new(request, params).add_server(body) + info = Devops::API2_0::Handler::Server.new(request).add_server() create_response(info) end + hash = {} + # Add devops server tags + # + # * *Request* + # - method : PUT + # - headers : + # - Content-Type: application/json + # - body : + # { + # "tags": {"tag name": "tag value"} + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } + # + # * *Returns* : + # 200 - Added + hash["PUT"] = lambda {|id| + check_privileges("server", "w") + Devops::API2_0::Handler::Server.new(request).set_tags(id) + create_response("Added") + } + + # Delete devops server tags + # + # * *Request* + # - method : DELETE + # - headers : + # - Content-Type: application/json + # - body : + # { + # "tags": {"tag name": "tag value"} + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } + # + # * *Returns* : + # 200 - Deleted + hash["DELETE"] = lambda {|id| + check_privileges("server", "w") + Devops::API2_0::Handler::Server.new(request).unset_tags(id) + create_response("Deleted") + } + app.multi_routes "/server/:id/tags", {:headers => [:content_type]}, hash + + # Set run_list to server + # + # * *Request* + # - method : PATCH + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "run_list": [ + # "role[role_1]", + # "recipe[recipe_1]" + # ], + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } + # + # * *Returns* : + # 200 - Updated + app.patch_with_headers "/server/:id/run_list", :headers => [:accept, :content_type] do |node_name| + check_privileges("server", "w") + Devops::API2_0::Handler::Server.new(request).set_server_run_list(node_name) + create_response("Run list has been changed") + end + puts "Server routes initialized" end diff --git a/devops-service/app/api2/routes/stack.rb b/devops-service/app/api2/routes/stack.rb index 0b0144c..abfe3cb 100644 --- a/devops-service/app/api2/routes/stack.rb +++ b/devops-service/app/api2/routes/stack.rb @@ -7,19 +7,18 @@ module Devops app.get_with_headers '/stacks', :headers => [:accept] do check_privileges("stack", "r") - json Devops::API2_0::Handler::Stack.new(request, params).stacks.map(&:to_hash) + json Devops::API2_0::Handler::Stack.new(request).stacks.map(&:to_hash) end app.get_with_headers '/stacks/provider/:provider', :headers => [:accept] do |provider| check_privileges("stack", "r") check_provider(provider) - json Devops::API2_0::Handler::Stack.new(request, params).stacks_for_provider(provider).map(&:to_hash) + json Devops::API2_0::Handler::Stack.new(request).stacks_for_provider(provider).map(&:to_hash) end app.post_with_headers "/stack", :headers => [:accept] do check_privileges("stack", "w") - object = create_object_from_json_body - m = Devops::API2_0::Handler::Stack.new(request, params).create_stack object + m = Devops::API2_0::Handler::Stack.new(request).create_stack object create_response "Created", m.to_hash, 201 end @@ -27,31 +26,53 @@ module Devops hash['GET'] = lambda { |stack_id| check_privileges("stack", "r") - json Devops::API2_0::Handler::Stack.new(request, params).stack(stack_id).to_hash + json Devops::API2_0::Handler::Stack.new(request).stack(stack_id).to_hash } hash['DELETE'] = lambda { |stack_id| check_privileges("stack", "w") - Devops::API2_0::Handler::Stack.new(request, params).delete_stack(stack_id) + Devops::API2_0::Handler::Stack.new(request).delete_stack(stack_id) create_response("Stack '#{stack_id}' has been removed") } app.multi_routes '/stack/:stack_id', {:headers => [:accept]}, hash app.post_with_headers "/stack/:stack_id/sync_details", :headers => [:accept] do |stack_id| check_privileges("stack", "w") - json Devops::API2_0::Handler::Stack.new(request, params).sync_details(stack_id).to_hash + json Devops::API2_0::Handler::Stack.new(request).sync_details(stack_id).to_hash end app.get_with_headers "/stack/:stack_id/resources", :headers => [:accept] do |stack_id| check_privileges("stack", "r") - json Devops::API2_0::Handler::Stack.new(request, params).resources(stack_id) + json Devops::API2_0::Handler::Stack.new(request).resources(stack_id) end app.get_with_headers "/stack/:stack_id/resources/:resource_id", :headers => [:accept] do |stack_id, resource_id| check_privileges("stack", "r") - json Devops::API2_0::Handler::Stack.new(request, params).resource(stack_id, resource_id) + json Devops::API2_0::Handler::Stack.new(request).resource(stack_id, resource_id) end + # Set run_list to stack + # + # * *Request* + # - method : PATCH + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # [ + # "role[role_1]", + # "recipe[recipe_1]" + # ] + # + # * *Returns* : + # 200 - Updated + app.patch_with_headers "/stack/:id/run_list", :headers => [:accept, :content_type] do |id| + check_privileges("stack", "w") + info = Devops::API2_0::Handler::Stack.new(request).set_run_list(id) + create_response(info) + end + + puts "Stack routes initialized" end diff --git a/devops-service/app/api2/routes/stack_presets.rb b/devops-service/app/api2/routes/stack_presets.rb index 85c2889..2619f6b 100644 --- a/devops-service/app/api2/routes/stack_presets.rb +++ b/devops-service/app/api2/routes/stack_presets.rb @@ -16,7 +16,7 @@ module Devops # app.get_with_headers "/stack_presets", :headers => [:accept] do # check_privileges("stack_template_presets", "r") - json Devops::API2_0::Handler::StackPreset.new(request, params).presets.map(&:to_hash) + json Devops::API2_0::Handler::StackPreset.new(request).presets.map(&:to_hash) end # Get information about stack_template_preset @@ -31,7 +31,7 @@ module Devops # app.get_with_headers "/stack_presets/:id", :headers => [:accept] do |id| # check_privileges("stack_template_presets", "r") - json Devops::API2_0::Handler::StackPreset.new(request, params).preset(id).to_hash + json Devops::API2_0::Handler::StackPreset.new(request).preset(id).to_hash end # Build stack template from preset @@ -57,8 +57,7 @@ module Devops app.post_with_headers "/stack_presets/:id/apply", :headers => [:accept] do |id| # check_privileges("stack_template_presets", "r") check_privileges('stack_template', 'w') - body = create_object_from_json_body - stack = Devops::API2_0::Handler::StackPreset.new(request, params).apply(id, body) + stack = Devops::API2_0::Handler::StackPreset.new(request).apply(id) create_response 'Created', stack.to_hash, 201 end diff --git a/devops-service/app/api2/routes/stack_template.rb b/devops-service/app/api2/routes/stack_template.rb index 322132d..66f538a 100644 --- a/devops-service/app/api2/routes/stack_template.rb +++ b/devops-service/app/api2/routes/stack_template.rb @@ -6,19 +6,18 @@ module Devops def self.registered(app) app.get_with_headers '/stack_templates', :headers => [:accept] do check_privileges('stack_template', 'r') - json Devops::API2_0::Handler::StackTemplate.new(request, params).stack_templates.map(&:to_hash) + json Devops::API2_0::Handler::StackTemplate.new(request).stack_templates.map(&:to_hash) end app.get_with_headers '/stack_templates/provider/:provider', :headers => [:accept] do |provider| check_privileges('stack_template', 'r') check_provider(provider) - json Devops::API2_0::Handler::StackTemplate.new(request, params).stack_templates_for_provider(provider).map(&:to_hash) + json Devops::API2_0::Handler::StackTemplate.new(request).stack_templates_for_provider(provider).map(&:to_hash) end app.post_with_headers "/stack_template", :headers => [:accept] do check_privileges('stack_template', 'w') - body = create_object_from_json_body - model = Devops::API2_0::Handler::StackTemplate.new(request, params).create_stack_template(body) + model = Devops::API2_0::Handler::StackTemplate.new(request).create_stack_template() create_response 'Created', model.to_hash, 201 end @@ -26,12 +25,12 @@ module Devops hash['GET'] = lambda {|stack_template_id| check_privileges('stack_template', 'r') - json Devops::API2_0::Handler::StackTemplate.new(request, params).stack_template(stack_template_id).to_hash + json Devops::API2_0::Handler::StackTemplate.new(request).stack_template(stack_template_id).to_hash } hash['DELETE'] = lambda {|stack_template_id| check_privileges('stack_template', 'w') - Devops::API2_0::Handler::StackTemplate.new(request, params).delete_stack_template(stack_template_id) + Devops::API2_0::Handler::StackTemplate.new(request).delete_stack_template(stack_template_id) create_response("Template '#{stack_template_id}' has been removed") } diff --git a/devops-service/app/api2/routes/tag.rb b/devops-service/app/api2/routes/tag.rb index 30fe0e4..ff8ffe0 100644 --- a/devops-service/app/api2/routes/tag.rb +++ b/devops-service/app/api2/routes/tag.rb @@ -17,9 +17,9 @@ module Devops # [ # "tag_1" # ] - hash["GET"] = lambda { + hash["GET"] = lambda {|node_name| check_privileges("server", "r") - json Devops::API2_0::Handler::Tag.new(params[:node_name]).tags() + json Devops::API2_0::Handler::Tag.new(request).tags(node_name) } # Set tags list to :node_name @@ -36,12 +36,10 @@ module Devops # # * *Returns* : # 200 - hash["POST"] = lambda { + hash["POST"] = lambda {|node_name| check_privileges("server", "w") - tags = create_object_from_json_body(Array) - check_array(tags, "Request body should be a not empty array of strings") - Devops::Version2_0::Handler::Tag.new(params[:node_name]).set_tags(tags) - create_response("Set tags for #{params[:node_name]}", tags: tags) + tags = Devops::API2_0::Handler::Tag.new(request).set_tags(node_name) + create_response("Set tags for #{node_name}", tags: tags) } # Delete tags from :node_name @@ -58,12 +56,10 @@ module Devops # # * *Returns* : # 200 - hash["DELETE"] = lambda { + hash["DELETE"] = lambda {|node_name| check_privileges("server", "w") - tags = create_object_from_json_body(Array) - check_array(tags, "Request body should be a not empty array of strings") - Devops::Version2_0::Handler::Tag.new(params[:node_name]).unset_tags(tags) - create_response("Deleted tags for #{params[:node_name]}", tags: tags) + tags = Devops::API2_0::Handler::Tag.new(request).unset_tags(node_name) + create_response("Deleted tags for #{node_name}", tags: tags) } app.multi_routes "/tags/:node_name", {:headers => [:accept, :content_type]}, hash diff --git a/devops-service/app/api2/routes/user.rb b/devops-service/app/api2/routes/user.rb index 6e88adf..e9ec0eb 100644 --- a/devops-service/app/api2/routes/user.rb +++ b/devops-service/app/api2/routes/user.rb @@ -33,10 +33,9 @@ module Devops # "id": "test" # } # ] - app.get_with_headers "/users", :headers => [:accept] do#, &Devops::API2_0::Handler::User.get_users + app.get_with_headers "/users", :headers => [:accept] do check_privileges("user", "r") - users = Devops::API2_0::Handler::User.new.users.map {|i| h = i.to_hash; h.delete("password"); h} - json users + json Devops::API2_0::Handler::User.new(request).users.map(&:to_hash) end # Create user @@ -55,13 +54,9 @@ module Devops # # * *Returns* : # 201 - Created - app.post_with_headers "/user", :headers => [:accept, :content_type] do#, &Devops::API2_0::Handler::User.create_user + app.post_with_headers "/user", :headers => [:accept, :content_type] do check_privileges("user", "w") - user = create_object_from_json_body - ["username", "password", "email"].each do |p| - check_string(user[p], "Parameter '#{p}' must be a not empty string") - end - Devops::API2_0::Handler::User.new.create(user) + Devops::API2_0::Handler::User.new(request).create create_response("Created", nil, 201) end @@ -75,23 +70,10 @@ module Devops # # * *Returns* : # 200 - Deleted - hash["DELETE"] = lambda { + hash["DELETE"] = lambda {|user| check_privileges("user", "w") - projects = Devops::Db.connector.projects_by_user params[:user] - if !projects.empty? - str = "" - projects.each do |p| - p.deploy_envs.each do |e| - str+="#{p.id}.#{e.identifier} " if e.users.include? params[:user] - end - end - logger.info projects - raise DependencyError.new "Deleting is forbidden: User is included in #{str}" - #return [400, "Deleting is forbidden: User is included in #{str}"] - end - - Devops::API2_0::Handler::User.new.delete(params[:user]) - create_response("User '#{params[:user]}' removed") + Devops::API2_0::Handler::User.new(request).delete(user) + create_response("User '#{user}' removed") } # Change user privileges @@ -109,17 +91,14 @@ module Devops # # * *Returns* : # 200 - Updated - hash["PUT"] = lambda { + hash["PUT"] = lambda {|user| check_privileges("user", "w") - data = create_object_from_json_body - cmd = check_string(data["cmd"], "Parameter 'cmd' should be a not empty string", true) || "" - privileges = check_string(data["privileges"], "Parameter 'privileges' should be a not empty string", true) || "" - Devops::API2_0::Handler::User.new.change_user_privileges(params[:user], cmd, privileges) + Devops::API2_0::Handler::User.new(request).change_user_privileges(user) create_response("Updated") } app.multi_routes "/user/:user", {:headers => [:accept, :content_type]}, hash - # Change user email/password + # Change user email # # * *Request* # - method : PUT @@ -128,23 +107,34 @@ module Devops # - Content-Type: application/json # - body : # { - # "email/password": "new user email/password", + # "email": "new user email", # } # # * *Returns* : # 200 - Updated - app.put_with_headers %r{\A/user/#{DevopsConfig::OBJECT_NAME}/(email|password)\z}, :headers => [:accept, :content_type] do#, &Devops::API2_0::Handler::User.change_user_email_or_password - check_privileges("user", "w") - action = File.basename(request.path) - u = File.basename(File.dirname(request.path)) - raise InvalidPrivileges.new("Access denied for '#{request.env['REMOTE_USER']}'") if u == Devops::Model::User::ROOT_USER_NAME and request.env['REMOTE_USER'] != Devops::Model::User::ROOT_USER_NAME + app.put_with_headers %r{\A/user/#{DevopsConfig::OBJECT_NAME}/email\z}, :headers => [:accept, :content_type] do |user| + check_privileges("user", "w") unless request.env['REMOTE_USER'] == user + Devops::API2_0::Handler::User.new(request).change_email(user) + create_response("Updated") + end - check_privileges("user", "w") unless request.env['REMOTE_USER'] == u - - body = create_object_from_json_body - p = check_string(body[action], "Parameter '#{action}' must be a not empty string") - h = Devops::API2_0::Handler::User.new - h.send("change_#{action}", body.merge('user_id' => u)) + # Change user password + # + # * *Request* + # - method : PUT + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "password": "new user password", + # } + # + # * *Returns* : + # 200 - Updated + app.put_with_headers %r{\A/user/#{DevopsConfig::OBJECT_NAME}/password\z}, :headers => [:accept, :content_type] do |user| + check_privileges("user", "w") unless request.env['REMOTE_USER'] == user + Devops::API2_0::Handler::User.new(request).change_password(user) create_response("Updated") end diff --git a/devops-service/app/api2/routes/v2.0.rb b/devops-service/app/api2/routes/v2.0.rb index f39a344..a8cd0de 100644 --- a/devops-service/app/api2/routes/v2.0.rb +++ b/devops-service/app/api2/routes/v2.0.rb @@ -9,6 +9,8 @@ require "exceptions/invalid_record" require "exceptions/record_not_found" require "exceptions/dependency_error" require "exceptions/conflict_exception" +require "exceptions/parser_error" +require "exceptions/validation_error" require 'core/devops-logger' require_relative "../helpers/version_2" @@ -39,8 +41,9 @@ module Devops logger = DevopsLogger.create(log_file, Logger::DEBUG) use Rack::CommonLogger, logger disable :raise_errors - # disable :dump_errors - set :show_exceptions, :after_handler + #set :show_exceptions, :after_handler + set :show_exceptions, false + #set :dump_errors, false logger.info "Development mode" end @@ -48,6 +51,18 @@ module Devops "Not found" end + error Devops::ValidationError do + e = env["sinatra.error"] + #logger.warn e.message + halt_response(e.message, 400) + end + + error Devops::ParserError do + e = env["sinatra.error"] + #logger.warn e.message + halt_response(e.message, 400) + end + error RecordNotFound do e = env["sinatra.error"] logger.warn e.message diff --git a/devops-service/app/api2/routes/views/report.erb b/devops-service/app/api2/routes/views/report.erb new file mode 100644 index 0000000..a0d27c0 --- /dev/null +++ b/devops-service/app/api2/routes/views/report.erb @@ -0,0 +1,18 @@ + + + <% unless @done %> + + <% end %> + + +
+ <%= @text %> ++ + + diff --git a/devops-service/app/devops-api2.rb b/devops-service/app/devops-api2.rb index 82e9f1f..a0baa8c 100644 --- a/devops-service/app/devops-api2.rb +++ b/devops-service/app/devops-api2.rb @@ -1,3 +1,4 @@ +require "lib/expire_handler" module Devops class DevopsApi2Application < Application @@ -35,8 +36,19 @@ module Devops Devops::Api2.set :scripts_dir, (config[:scripts_dir] || File.join(config[:devops_dir], "files/scripts")) [:keys_dir, :scripts_dir].each {|key| d = Devops::Api2.settings.send(key); FileUtils.mkdir_p(d) unless File.exists?(d) } init_mongo - Devops::Api2.settings.mongo.create_root_user + mongo = Devops::Api2.settings.mongo + mongo.create_root_user ::Provider::ProviderFactory.init(config) + ::Provider::ProviderFactory.all.each do |p| + next if p.certificate_path.nil? or p.certificate_path.empty? + begin + mongo.key p.ssh_key, Devops::Model::Key::SYSTEM + rescue RecordNotFound => e + k = Devops::Model::Key.new({"id" => p.ssh_key, "path" => p.certificate_path, "scope" => Devops::Model::Key::SYSTEM}) + mongo.key_insert k + end + end + ExpireHandler.init Stubber.stub_providers!(config[:stub_providers]) end diff --git a/devops-service/commands/deploy.rb b/devops-service/commands/deploy.rb index d97828a..42c0274 100644 --- a/devops-service/commands/deploy.rb +++ b/devops-service/commands/deploy.rb @@ -4,48 +4,60 @@ require "commands/ssh" module DeployCommands def deploy_server_proc - lambda do |out, s, mongo, tags| - begin - old_tags_str = nil - new_tags_str = nil - unless tags.empty? - old_tags_str = KnifeCommands.tags_list(s.chef_node_name).join(" ") - out << "Server tags: #{old_tags_str}\n" - KnifeCommands.tags_delete(s.chef_node_name, old_tags_str) + lambda do |out, s, mongo, tags, deploy_info| + old_tags_str = nil + new_tags_str = nil + unless tags.empty? + old_tags_str = KnifeCommands.tags_list(s.chef_node_name).join(" ") + out << "Server tags: #{old_tags_str}\n" + KnifeCommands.tags_delete(s.chef_node_name, old_tags_str) - new_tags_str = tags.join(" ") - out << "Server new tags: #{new_tags_str}\n" - cmd = KnifeCommands.tags_create(s.chef_node_name, new_tags_str) - unless cmd[1] - m = "Error: Cannot add tags '#{new_tags_str}' to server '#{s.chef_node_name}'" - logger.error(m) - out << m + "\n" - return 3 - end - logger.info("Set tags for '#{s.chef_node_name}': #{new_tags_str}") + new_tags_str = tags.join(" ") + out << "Server new tags: #{new_tags_str}\n" + cmd = KnifeCommands.tags_create(s.chef_node_name, new_tags_str) + unless cmd[1] + m = "Error: Cannot add tags '#{new_tags_str}' to server '#{s.chef_node_name}'" + DevopsLogger.logger.error(m) + out << m + "\n" + return 3 end - - k = mongo.key s.key - r = deploy_server out, s, k.path - - unless tags.empty? - out << "Restore tags\n" - cmd = KnifeCommands.tags_delete(s.chef_node_name, new_tags_str) - logger.info("Deleted tags for #{s.chef_node_name}: #{new_tags_str}") - cmd = KnifeCommands.tags_create(s.chef_node_name, old_tags_str) - logger.info("Set tags for #{s.chef_node_name}: #{old_tags_str}") - end - return r - rescue IOError => e - logger.error e.message - return 4 + DevopsLogger.logger.info("Set tags for '#{s.chef_node_name}': #{new_tags_str}") end + + k = mongo.key s.key + r = deploy_server out, s, k.path, deploy_info + + unless tags.empty? + out << "Restore tags\n" + cmd = KnifeCommands.tags_delete(s.chef_node_name, new_tags_str) + DevopsLogger.logger.info("Deleted tags for #{s.chef_node_name}: #{new_tags_str}") + cmd = KnifeCommands.tags_create(s.chef_node_name, old_tags_str) + DevopsLogger.logger.info("Set tags for #{s.chef_node_name}: #{old_tags_str}") + end + return r end end - def deploy_server out, server, cert_path + def deploy_server out, server, cert_path, deploy_info + out << "Before deploy hooks...\n" + res = server.run_hook(:before_deploy, out, deploy_info) + out << "Done\n" out << "\nRun chef-client on '#{server.chef_node_name}'\n" - cmd = "chef-client" + cmd = "chef-client --no-color" + if deploy_info["use_json_file"] + deploy_info.delete["use_json_file"] + out << "Build information:\n" + json = JSON.pretty_generate(deploy_info) + out << json + out << "\n" + file = "#{server.project}_#{server.deploy_env}_#{Time.new.to_i}" + dir = DevopsConfig.config[:project_info_dir] + File.open(File.join(dir, file), "w") do |f| + f.write json + end + out.flush if out.respond_to?(:flush) + cmd << " -j http://#{DevopsConfig.config[:address]}:#{DevopsConfig.config[:port]}/#{DevopsConfig.config[:url_prefix]}/v2.0/chef/client/data/#{file}" + end ip = if server.public_ip.nil? server.private_ip else @@ -55,7 +67,15 @@ module DeployCommands out.flush if out.respond_to?(:flush) lline = KnifeCommands.ssh_stream(out, cmd, ip, server.remote_user, cert_path) r = /Chef\sClient\sfinished/i - return (lline[r].nil? ? 1 : 0) + if lline[r].nil? + 1 + else + out << "After deploy hooks...\n" + res = server.run_hook(:after_deploy, out, deploy_info) + out << "Done\n" + 0 + end + end end diff --git a/devops-service/commands/server.rb b/devops-service/commands/server.rb index d690f93..a67aa89 100644 --- a/devops-service/commands/server.rb +++ b/devops-service/commands/server.rb @@ -1,3 +1,4 @@ +require 'set' require "commands/knife_commands" require "commands/deploy" require "exceptions/record_not_found" @@ -7,130 +8,106 @@ module ServerCommands include DeployCommands def create_server_proc - lambda do |out, s, provider, mongo| + lambda do |out, s, provider| + mongo = ::Devops::Db.connector begin out << "Create server...\n" out.flush if out.respond_to?(:flush) unless provider.create_server(s, out) return 3 end - mongo.server_insert s + s.create out.flush if out.respond_to?(:flush) - logger.info "Server with parameters: #{s.to_hash.inspect} is running" + DevopsLogger.logger.info "Server with parameters: #{s.to_hash.inspect} is running" key = mongo.key(s.key) - s.chef_node_name = provider.create_default_chef_node_name(s) if s.chef_node_name.nil? - return two_phase_bootstrap(s, out, provider, mongo, key.path, logger) - rescue IOError => e - logger.error e.message - logger.warn roll_back(s, provider) + return two_phase_bootstrap(s, out, provider, key.path) + rescue => e + DevopsLogger.logger.error e.message + DevopsLogger.logger.warn roll_back(s, provider) mongo.server_delete s.id return 5 end end end - def create_server_proc - lambda do |out, s, provider, mongo| - begin - out << "Create server...\n" - out.flush if out.respond_to?(:flush) - unless provider.create_server(s, out) - return 3 - end - mongo.server_insert s - out.flush if out.respond_to?(:flush) - logger.info "Server with parameters: #{s.to_hash.inspect} is running" + def create_server project, env, params, user, out + provider = ::Provider::ProviderFactory.get(env.provider) + mongo = ::Devops::Db.connector + begin + out << "Create server...\n" + out.flush if out.respond_to?(:flush) + + s = Devops::Model::Server.new + s.provider = provider.name + s.project = project.id + s.deploy_env = env.identifier + s.run_list = params["run_list"] || [] + s.chef_node_name = params["name"] + s.key = params["key"] || provider.ssh_key + + i = mongo.image env.image + s.remote_user = i.remote_user + s.created_by = user + + return 3 unless s.create(provider, env.image, env.flavor, env.subnets, env.groups, out) + out.flush if out.respond_to?(:flush) + DevopsLogger.logger.info "Server with parameters: #{s.to_hash.inspect} is running" + unless params["without_bootstrap"] + s.run_list = Set.new.merge(project.run_list).merge(env.run_list).merge(s.run_list) key = mongo.key(s.key) s.chef_node_name = provider.create_default_chef_node_name(s) if s.chef_node_name.nil? - out << "\n\nBootstrap..." - out.flush if out.respond_to?(:flush) - run_list = s.options[:run_list] - s.options[:run_list] = provider.run_list - out << "\nBootstrap with provider run list: #{s.options[:run_list].inspect}" - status = bootstrap(s, out, key.path, logger) - out.flush if out.respond_to?(:flush) - if status == 0 - mongo.server_set_chef_node_name s - logger.info "Server with id '#{s.id}' is bootstraped" - if check_server(s) - out << "Server #{s.chef_node_name} is created" - else - out << roll_back(s, provider) - mongo.server_delete s.id - return 5 - end - out << "\n" - out.flush if out.respond_to?(:flush) - - out << "\nAdd project run list: #{run_list.inspect}" - s.options[:run_list] += run_list - KnifeCommands.set_run_list(s.chef_node_name, s.options[:run_list]) - status = deploy_server(out, s, key.path) - if status != 0 - msg = "Failed on chef-client with project run list, server with id '#{s.id}'" - logger.error msg - out << "\n" + msg + "\n" - mongo.server_delete s.id - end - return status - else - msg = "Failed while bootstraping server with id '#{s.id}'" - logger.error msg - out << "\n" + msg + "\n" - out << roll_back(s, provider) - mongo.server_delete s.id - status - end - rescue IOError => e - logger.error e.message - logger.warn roll_back(s, provider) - mongo.server_delete s.id - return 5 - end - end - end - - def two_phase_bootstrap s, out, provider, mongo, cert_path, logger - out << "\n\nBootstrap..." - out.flush if out.respond_to?(:flush) - run_list = s.options[:run_list] - s.options[:run_list] = provider.run_list - out << "\nBootstrap with provider run list: #{s.options[:run_list].inspect}" - status = bootstrap(s, out, cert_path, logger) - out.flush if out.respond_to?(:flush) - if status == 0 - mongo.server_set_chef_node_name s - logger.info "Server with id '#{s.id}' is bootstraped" - if check_server(s) - out << "Server #{s.chef_node_name} is created" - else - out << roll_back(s, provider) - mongo.server_delete s.id - return 5 - end - out << "\n" - out.flush if out.respond_to?(:flush) - - out << "\nAdd project run list: #{run_list.inspect}" - s.options[:run_list] += run_list - KnifeCommands.set_run_list(s.chef_node_name, s.options[:run_list]) - status = deploy_server(out, s, cert_path) - if status != 0 - msg = "Failed on chef-client with project run list, server with id '#{s.id}'" - logger.error msg - out << "\n" + msg + "\n" - end + return two_phase_bootstrap(s, provider.run_list, i.bootstrap_template, key.path, out) + else + return 0 + end + rescue => e + DevopsLogger.logger.error e.message + DevopsLogger.logger.warn roll_back(s, provider) + mongo.server_delete s.id + return 5 + end + end + + def two_phase_bootstrap s, provider_run_list, bootstrap_template, cert_path, out + mongo = ::Devops::Db.connector + out << "\n\nBootstrap...\n" + out.flush if out.respond_to?(:flush) + status = bootstrap(s, out, bootstrap_template, provider_run_list, cert_path) + out.flush if out.respond_to?(:flush) + if status == 0 + DevopsLogger.logger.info "Server with id '#{s.id}' is bootstraped" + if check_server(s) + out << "Server #{s.chef_node_name} is created" else - msg = "Failed while bootstraping server with id '#{s.id}'" - logger.error msg - out << "\n" + msg + "\n" out << roll_back(s, provider) mongo.server_delete s.id + return 5 end - return status + out << "\n" + out.flush if out.respond_to?(:flush) + + run_list = s.run_list + provider_run_list + out << "\nRun list: #{run_list.inspect}" +# s.options[:run_list] += run_list + KnifeCommands.set_run_list(s.chef_node_name, run_list) + status = deploy_server(out, s, cert_path) + if status != 0 + msg = "Failed on chef-client with project run list, server with id '#{s.id}'" + DevopsLogger.logger.error msg + out << "\n" + msg + "\n" + end + else + msg = "Failed while bootstraping server with id '#{s.id}'" + DevopsLogger.logger.error msg + out << "\n" + msg + "\n" + out << roll_back(s, provider) + mongo.server_delete s.id + end + return status end - def extract_servers provider, project, env, params, user, mongo + def extract_servers provider, project, env, params, user + mongo = ::Devops::Db.connector flavors = provider.flavors projects = {} env_name = env.identifier @@ -167,12 +144,14 @@ module ServerCommands i = mongo.image env.image flavor = flavors.detect {|f| f["id"] == env.flavor} raise RecordNotFound.new("Flavor with id '#{env.flavor}' not found") if flavor.nil? + rl = Set.new + rl.merge(project.run_list).merge(env.run_list) o = { :image => i, :name => params["name"], :flavor => flavor["id"], :groups => params["groups"] || env.groups, - :run_list => env.run_list, + :run_list => rl, :subnets => env.subnets, :key => params["key"] } @@ -186,15 +165,16 @@ module ServerCommands s.provider = provider.name s.project = project_name s.deploy_env = env_name + s.run_list = params["run_list"] || [] s.remote_user = image.remote_user - s.chef_node_name = info[:name] || provider.create_default_chef_node_name(s) + s.chef_node_name = info[:name] s.key = info[:key] || provider.ssh_key s.options = { :image => image.id, :flavor => info[:flavor], :name => info[:name], :groups => info[:groups], - :run_list => info[:run_list], + :run_list => info[:run_list].merge(s.run_list), :bootstrap_template => image.bootstrap_template, :subnets => info[:subnets] } @@ -215,11 +195,15 @@ module ServerCommands KnifeCommands.chef_node_list.include?(s.chef_node_name) and KnifeCommands.chef_client_list.include?(s.chef_node_name) end - def bootstrap s, out, cert_path, logger + def bootstrap s, out, bootstrap_template, run_list, cert_path + out << "Before bootstrap hooks...\n" + res = s.run_hook(:before_bootstrap, out) + out << "Done\n" if s.private_ip.nil? out << "Error: Private IP is null" return false end + out << "\nBootstrap with run list: #{run_list.inspect}\n" ja = { :provider => s.provider, :devops_host => `hostname`.strip @@ -227,12 +211,12 @@ module ServerCommands bootstrap_options = [ "-x #{s.remote_user}", "-i #{cert_path}", - "--json-attributes '#{ja.to_json}'" + "--json-attributes '#{ja.to_json}'", + "-N #{s.chef_node_name}" ] bootstrap_options.push "--sudo" unless s.remote_user == "root" - bootstrap_options.push "-N #{s.chef_node_name}" if s.chef_node_name - bootstrap_options.push "-d #{s.options[:bootstrap_template]}" if s.options[:bootstrap_template] - bootstrap_options.push "-r #{s.options[:run_list].join(",")}" unless s.options[:run_list].empty? + bootstrap_options.push "-d #{bootstrap_template}" if bootstrap_template + bootstrap_options.push "-r #{run_list.join(",")}" unless run_list.empty? ip = s.private_ip unless s.public_ip.nil? || s.public_ip.strip.empty? ip = s.public_ip @@ -249,7 +233,7 @@ module ServerCommands if i == 120 out << "\nCan not connect to #{s.remote_user}@#{ip}" out << "\n" + res - logger.error "Can not connect with command 'ssh -i #{cert_path} #{s.remote_user}@#{ip}':\n#{res}" + DevopsLogger.logger.error "Can not connect with command 'ssh -i #{cert_path} #{s.remote_user}@#{ip}':\n#{res}" return false end raise ArgumentError.new("Can not connect with command '#{cmd}' ") unless $?.success? @@ -257,7 +241,17 @@ module ServerCommands retry end - return KnifeCommands.knife_bootstrap(out, ip, bootstrap_options) + r = KnifeCommands.knife_bootstrap(out, ip, bootstrap_options) + if r == 0 + out << "Chef node name: #{s.chef_node_name}\n" + ::Devops::Db.connector.server_set_chef_node_name s + out << "Chef node name has been updated\n" + out << "After bootstrap hooks...\n" + res = s.run_hook(:after_bootstrap, out) + out << "Done\n" + else + end + r end def self.unbootstrap s, cert_path @@ -266,7 +260,7 @@ module ServerCommands r = `ssh -i #{cert_path} -q #{s.remote_user}@#{s.private_ip} rm -Rf /etc/chef` raise(r) unless $?.success? rescue => e - logger.error "Unbootstrap error: " + e.message + DevopsLogger.logger.error "Unbootstrap error: " + e.message i += 1 sleep(1) retry unless i == 5 @@ -275,15 +269,16 @@ module ServerCommands nil end - def delete_server s, mongo, logger + def delete_server s + mongo = ::Devops::Db.connector if s.static? if !s.chef_node_name.nil? - cert = ::Devops::Db.connector.key s.key + cert = mongo.key s.key ServerCommands.unbootstrap(s, cert.path) end mongo.server_delete s.id msg = "Static server '#{s.id}' is removed" - logger.info msg + DevopsLogger.logger.info msg return msg, nil end r = delete_from_chef_server(s.chef_node_name) @@ -292,12 +287,12 @@ module ServerCommands r[:server] = provider.delete_server s rescue Fog::Compute::OpenStack::NotFound, Fog::Compute::AWS::NotFound r[:server] = "Server with id '#{s.id}' not found in '#{provider.name}' servers" - logger.warn r[:server] + DevopsLogger.logger.warn r[:server] end mongo.server_delete s.id info = "Server '#{s.id}' with name '#{s.chef_node_name}' for project '#{s.project}-#{s.deploy_env}' is removed" - logger.info info - r.each{|key, log| logger.info("#{key} - #{log}")} + DevopsLogger.logger.info info + r.each{|key, log| DevopsLogger.logger.info("#{key} - #{log}")} return info, r end diff --git a/devops-service/core/devops-logger.rb b/devops-service/core/devops-logger.rb index 8963f2b..39a8dcd 100644 --- a/devops-service/core/devops-logger.rb +++ b/devops-service/core/devops-logger.rb @@ -13,4 +13,8 @@ class DevopsLogger def self.logger @_logger end + + def self.logger= logger + @_logger = logger + end end diff --git a/devops-service/db/mongo/connectors/key.rb b/devops-service/db/mongo/connectors/key.rb index 7f28e8f..c2bfd9e 100644 --- a/devops-service/db/mongo/connectors/key.rb +++ b/devops-service/db/mongo/connectors/key.rb @@ -1,3 +1,4 @@ +require "db/mongo/models/key" module Connectors class Key < Base include Helpers::InsertCommand, diff --git a/devops-service/db/mongo/connectors/project.rb b/devops-service/db/mongo/connectors/project.rb index 9e46ef5..0c279da 100644 --- a/devops-service/db/mongo/connectors/project.rb +++ b/devops-service/db/mongo/connectors/project.rb @@ -93,6 +93,16 @@ module Connectors project end + def set_project_env_run_list(project_id, env, run_list) + Helpers::RunList.new(run_list).validate! + @collection.update({"_id" => project_id, "deploy_envs.identifier" => env}, {"$set" => {"deploy_envs.$.run_list" => run_list}}) + end + + def set_project_run_list(project_id, env, run_list) + Helpers::RunList.new(run_list).validate! + @collection.update({"_id" => project_id}, {"$set" => {run_list: run_list}}) + end + private def list(query={}, query_options={}) diff --git a/devops-service/db/mongo/connectors/report.rb b/devops-service/db/mongo/connectors/report.rb index b5ea994..6e43683 100644 --- a/devops-service/db/mongo/connectors/report.rb +++ b/devops-service/db/mongo/connectors/report.rb @@ -11,7 +11,11 @@ module Connectors def save_report r r.created_at = Time.new - collection.insert(r.to_mongo_hash) + collection.insert(r.to_mongo_hash) unless report_exists?(r.id) + end + + def report_exists? id + !collection.find({"_id" => id}, fields: ["_id" => true]).to_a.empty? end def reports options={} diff --git a/devops-service/db/mongo/connectors/server.rb b/devops-service/db/mongo/connectors/server.rb index 508abd8..8adb509 100644 --- a/devops-service/db/mongo/connectors/server.rb +++ b/devops-service/db/mongo/connectors/server.rb @@ -71,6 +71,10 @@ module Connectors collection.remove('stack' => stack_id) end + def set_server_run_list(id, run_list) + @collection.update({"_id" => id}, {"$set" => {"run_list" => run_list}}) + end + private def model_from_bson(bson) diff --git a/devops-service/db/mongo/connectors/stack.rb b/devops-service/db/mongo/connectors/stack.rb index 3024821..241c5b8 100644 --- a/devops-service/db/mongo/connectors/stack.rb +++ b/devops-service/db/mongo/connectors/stack.rb @@ -14,6 +14,10 @@ module Connectors list(options) end + def set_stack_run_list id, run_list + collection.update({"_id" => id}, {"$set" => {"run_list" => run_list}}) + end + private def model_from_bson(bson) diff --git a/devops-service/db/mongo/connectors/user.rb b/devops-service/db/mongo/connectors/user.rb index bf83e69..526839d 100644 --- a/devops-service/db/mongo/connectors/user.rb +++ b/devops-service/db/mongo/connectors/user.rb @@ -17,10 +17,10 @@ module Connectors model_from_bson(u) end - def users(ids=nil) + def users(ids=nil, options={}) query = {} query['_id'] = {'$in' => ids} if ids.is_a?(Array) - list(query) + list(query, options) end def users_names(ids=nil) diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb index 7a88483..41e36e1 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb @@ -1,14 +1,16 @@ require "db/mongo/models/mongo_model" require "providers/provider_factory" require "commands/deploy_env" +require "db/mongo/models/model_with_provider" module Devops module Model class DeployEnvBase < MongoModel include DeployEnvCommands + include ModelWithProvider - attr_accessor :identifier, :run_list, :expires, :provider, :users, :chef_env + attr_accessor :identifier, :run_list, :expires, :users, :chef_env def initialize d={} self.identifier = d["identifier"] @@ -30,15 +32,10 @@ module Devops } end - def provider_instance - @provider_instance ||= ::Provider::ProviderFactory.get(self.provider) - end - def build_error_message(message) "Deploy environment '#{self.identifier}'. " + message end - # class methods class << self diff --git a/devops-service/db/mongo/models/model_with_provider.rb b/devops-service/db/mongo/models/model_with_provider.rb new file mode 100644 index 0000000..41fe401 --- /dev/null +++ b/devops-service/db/mongo/models/model_with_provider.rb @@ -0,0 +1,15 @@ +require "providers/provider_factory" + +module Devops + module Model + module ModelWithProvider + + attr_accessor :provider + + def provider_instance + @provider_instance ||= Provider::ProviderFactory.get(self.provider) + end + end + end +end + diff --git a/devops-service/db/mongo/models/mongo_model.rb b/devops-service/db/mongo/models/mongo_model.rb index a880934..cf37bf3 100644 --- a/devops-service/db/mongo/models/mongo_model.rb +++ b/devops-service/db/mongo/models/mongo_model.rb @@ -119,7 +119,7 @@ module Devops end def self.validators - @validators || [] + @validators ||= [] end # all exceptions are handled in @validate! method diff --git a/devops-service/db/mongo/models/project.rb b/devops-service/db/mongo/models/project.rb index 1fe6345..a5fb3f0 100644 --- a/devops-service/db/mongo/models/project.rb +++ b/devops-service/db/mongo/models/project.rb @@ -4,7 +4,7 @@ require "db/mongo/models/deploy_env/deploy_env_multi" require "db/mongo/models/mongo_model" require "json" require "hooks" -require "lib/project/handler/types_factory" +require "lib/project/type/types_factory" module Devops module Model @@ -18,14 +18,17 @@ module Devops define_hook :before_delete define_hook :after_delete - attr_accessor :id, :deploy_envs, :type, :archived, :description + attr_accessor :id, :deploy_envs, :type, :archived, :description, :run_list types :id => {:type => String, :empty => false}, :deploy_envs => {:type => Array, :value_type => false, :empty => false}, - :description => {:type => String, :empty => true, :nil => true} + :description => {:type => String, :empty => true, :nil => true}, + :run_list => {:type => Array, :value_type => String, :empty => true, :nil => false} MULTI_TYPE = "multi" + set_validators ::Validators::DeployEnv::RunList + def self.fields ["deploy_envs", "type", "description"] end @@ -36,9 +39,10 @@ module Devops self.type = p["type"] || Devops::GenericType.name self.description = p["description"] self.archived = p["archived"] || false + self.run_list = p["run_list"] || [] - handler = Devops::TypesFactory.type self.type - handler.prepare(self) unless handler.nil? + @handler = Devops::TypesFactory.type self.type + @handler.prepare(self) env_class = ( self.multi? ? DeployEnvMulti : DeployEnvFactory ) unless p["deploy_envs"].nil? @@ -132,6 +136,10 @@ module Devops h end + def deploy_info deploy_env, build_number + {} + end + def to_hash_without_id h = {} h["deploy_envs"] = self.deploy_envs.map {|e| e.to_hash} unless self.deploy_envs.nil? @@ -163,6 +171,10 @@ module Devops res end + def extract_servers env, params + @handler.extract_servers(self, env, params) + end + def self.build_from_bson p p["name"] = p["_id"] Project.new p diff --git a/devops-service/db/mongo/models/report.rb b/devops-service/db/mongo/models/report.rb index 5485dd2..ce40e92 100644 --- a/devops-service/db/mongo/models/report.rb +++ b/devops-service/db/mongo/models/report.rb @@ -10,7 +10,7 @@ module Devops PROJECT_TEST_TYPE = 4 STACK_TYPE = 5 - attr_accessor :id, :file, :created_at, :created_by, :project, :deploy_env, :type + attr_accessor :id, :file, :created_at, :updated_at, :created_by, :project, :deploy_env, :type, :chef_node_name, :host def initialize r self.id = r["_id"] @@ -20,16 +20,23 @@ module Devops self.deploy_env = r["deploy_env"] self.type = r["type"] self.created_at = r["created_at"] + self.chef_node_name = r["chef_node_name"] + self.host = r["host"] + self.created_at = r["created_at"].localtime unless r["created_at"].nil? + self.updated_at = r["updated_at"].localtime unless r["updated_at"].nil? end def to_hash_without_id { "file" => self.file, "created_at" => self.created_at, + "updated_at" => self.updated_at, "created_by" => self.created_by, "project" => self.project, "deploy_env" => self.deploy_env, - "type" => self.type + "type" => self.type, + "chef_node_name" => self.chef_node_name, + "host" => self.host } end diff --git a/devops-service/db/mongo/models/server.rb b/devops-service/db/mongo/models/server.rb index e4da64d..9055fdd 100644 --- a/devops-service/db/mongo/models/server.rb +++ b/devops-service/db/mongo/models/server.rb @@ -1,11 +1,30 @@ require "db/mongo/models/mongo_model" +require "hooks" +require "db/mongo/models/model_with_provider" + module Devops module Model class Server < MongoModel - attr_accessor :provider, :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by, :reserved_by, :stack - attr_accessor :options, :static, :key + include ModelWithProvider + include Hooks + #params: + # out - container for output data + # deploy_info - hash with deploy data + define_hook :before_deploy + define_hook :after_deploy + + define_hook :before_create + define_hook :after_create + + #params: + # out - container for output data + define_hook :before_bootstrap + define_hook :after_bootstrap + + attr_accessor :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by, :reserved_by, :stack, :run_list + attr_accessor :key types :id => {:type => String, :empty => false}, :provider => {:type => String, :empty => false}, @@ -18,10 +37,13 @@ module Devops :created_by => {:type => String, :empty => false}, :chef_node_name => {:type => String, :empty => true}, :reserved_by => {:type => String, :empty => true}, - :stack => {:type => String, :nil => true} + :stack => {:type => String, :nil => true}, + :run_list => {:type => Array, :value_type => String, :empty => true, :nil => false} + + set_validators ::Validators::DeployEnv::RunList def self.fields - ["chef_node_name", "project", "deploy_env", "provider", "remote_user", "private_ip", "public_ip", "created_at", "created_by", "static", "key", "reserved_by"] + ["chef_node_name", "project", "deploy_env", "provider", "remote_user", "private_ip", "public_ip", "created_at", "created_by", "key", "reserved_by"] end def initialize s={} @@ -35,10 +57,19 @@ module Devops self.private_ip = s["private_ip"] self.created_at = s["created_at"] self.created_by = s["created_by"] - self.static = s["static"] self.key = s["key"] self.reserved_by = s["reserved_by"] self.stack = s["stack"] + self.run_list = s["run_list"] || [] + end + + def create provider, image, flavor, subnets, groups, out + res = {} + res[:before] = self.run_hook :before_create + return false unless provider.create_server(self, image, flavor, subnets, groups, out) + Devops::Db.connector.server_insert self + res[:after] = self.run_hook :after_create + res end def validate! @@ -57,10 +88,10 @@ module Devops "public_ip" => self.public_ip, "created_at" => self.created_at, "created_by" => self.created_by, - "static" => self.static, "key" => self.key, "reserved_by" => self.reserved_by, - "stack" => stack + "stack" => stack, + "run_list" => self.run_list }.delete_if{|k,v| v.nil?} end @@ -87,7 +118,7 @@ module Devops end def static? - self.static || false + (self.provider == Provider::Static::PROVIDER) || false end end diff --git a/devops-service/db/mongo/models/stack/stack_base.rb b/devops-service/db/mongo/models/stack/stack_base.rb index d95c435..92e4dca 100644 --- a/devops-service/db/mongo/models/stack/stack_base.rb +++ b/devops-service/db/mongo/models/stack/stack_base.rb @@ -1,9 +1,12 @@ +require "db/mongo/models/model_with_provider" module Devops module Model class StackBase < MongoModel + include ModelWithProvider + attr_accessor :id, :project, :deploy_env, :stack_template, - :cloud_stack_id, :provider, :parameters, :details, :owner + :cloud_stack_id, :parameters, :details, :owner, :run_list types id: {type: String, empty: false}, provider: {type: String, empty: false}, @@ -11,7 +14,8 @@ module Devops deploy_env: {type: String}, stack_template: {type: String, empty: false}, cloud_stack_id: {type: String, nil: true}, - owner: {type: String} + owner: {type: String}, + run_list: {type: Array, value_type: String, empty: true, nil: true} # details: {type: Hash, nil: true} # Hash type isn't supported yet def initialize attrs={} @@ -25,6 +29,7 @@ module Devops self.parameters = attrs['parameters'] self.details = attrs['details'] self.owner = attrs['owner'] + self.run_list = attrs['run_list'] self end @@ -44,27 +49,27 @@ module Devops def create_stack_in_cloud! begin - self.cloud_stack_id = provider_class.create_stack(self) + self.cloud_stack_id = provider_instance.create_stack(self) rescue ProviderErrors::NameConflict raise InvalidRecord.new "Duplicate key error: stack with name '#{id}' already exists in cloud" end end def delete_stack_in_cloud! - provider_class.delete_stack(self) + provider_instance.delete_stack(self) end def sync_details! - self.details = provider_class.stack_details(self).attributes + self.details = provider_instance.stack_details(self).attributes end def resources - provider_class.stack_resources(self) + provider_instance.stack_resources(self) end # resource_id is logical def resource(resource_id) - provider_class.stack_resource(self, resource_id) + provider_instance.stack_resource(self, resource_id) end def stack_status @@ -84,7 +89,6 @@ module Devops end class << self - attr_accessor :provider # attrs should include: # - id (String) @@ -103,12 +107,6 @@ module Devops end end - private - - def provider_class - Provider::ProviderFactory.get(provider) - end - end end end diff --git a/devops-service/db/mongo/models/stack/stack_ec2.rb b/devops-service/db/mongo/models/stack/stack_ec2.rb index 693b9d2..a3cff24 100644 --- a/devops-service/db/mongo/models/stack/stack_ec2.rb +++ b/devops-service/db/mongo/models/stack/stack_ec2.rb @@ -1,7 +1,12 @@ module Devops module Model class StackEc2 < StackBase - self.provider = 'ec2' + + def initialize attr={} + self.provider = 'ec2' + super(attr) + end + end end end diff --git a/devops-service/db/mongo/models/stack/stack_openstack.rb b/devops-service/db/mongo/models/stack/stack_openstack.rb index 13818d9..fc95d8e 100644 --- a/devops-service/db/mongo/models/stack/stack_openstack.rb +++ b/devops-service/db/mongo/models/stack/stack_openstack.rb @@ -1,11 +1,19 @@ module Devops module Model class StackOpenstack < StackBase - self.provider = 'openstack' + + def initialize attr={} + self.provider = 'openstack' + super(attr) + end def stack_status details[:stack_status] || details['stack_status'] if details end + + def self.provider + 'openstack' + end end end end diff --git a/devops-service/db/mongo/mongo_connector.rb b/devops-service/db/mongo/mongo_connector.rb index 2ccd5a8..ba8342b 100644 --- a/devops-service/db/mongo/mongo_connector.rb +++ b/devops-service/db/mongo/mongo_connector.rb @@ -13,16 +13,16 @@ class MongoConnector delegate( [:images, :image, :image_insert, :image_delete, :image_update] => :images_connector, [:stack_templates, :stack_template, :stack_template_insert, :stack_template_delete, :stack_template_update] => :stack_templates_connector, - [:stacks, :stack, :stack_insert, :stack_delete, :stack_update] => :stacks_connector, + [:stacks, :stack, :stack_insert, :stack_delete, :stack_update, :set_stack_run_list] => :stacks_connector, [:available_images, :add_available_images, :delete_available_images] => :filters_connector, [:project, :projects_all, :projects, :project_names_with_envs, :projects_by_image, :projects_by_user, :project_insert, :project_update, - :project_delete, :is_project_exists?, :check_project_auth] => :projects_connector, + :project_delete, :is_project_exists?, :check_project_auth, :set_project_run_list, :set_project_env_run_list] => :projects_connector, [:project_templates, :project_template_insert, :project_template_update, :project_template_delete] => :projects_templates_connector, [:servers_find, :servers, :stack_servers, :servers_by_names, :server_by_instance_id, :server_by_chef_node_name, :servers_by_key, :server_insert, - :server_delete, :server_update, :server_set_chef_node_name, :stack_servers_delete] => :servers_connector, + :server_delete, :server_update, :server_set_chef_node_name, :stack_servers_delete, :set_server_run_list] => :servers_connector, [:user_auth, :user, :users, :users_names, :user_insert, :user_delete, :user_update, :create_root_user, :check_user_privileges] => :users_connector, [:keys, :key, :key_insert, :key_delete] => :keys_connector, diff --git a/devops-service/exceptions/deploy_info_error.rb b/devops-service/exceptions/deploy_info_error.rb new file mode 100644 index 0000000..b6f899c --- /dev/null +++ b/devops-service/exceptions/deploy_info_error.rb @@ -0,0 +1,3 @@ +class DeployInfoError < Exception + +end diff --git a/devops-service/exceptions/parser_error.rb b/devops-service/exceptions/parser_error.rb new file mode 100644 index 0000000..3afda7a --- /dev/null +++ b/devops-service/exceptions/parser_error.rb @@ -0,0 +1,4 @@ +module Devops + class ParserError < Exception + end +end diff --git a/devops-service/exceptions/validation_error.rb b/devops-service/exceptions/validation_error.rb new file mode 100644 index 0000000..9f275e9 --- /dev/null +++ b/devops-service/exceptions/validation_error.rb @@ -0,0 +1,12 @@ +module Devops + class ValidationError < Exception + + attr_accessor :json + + def initialize msg, json=true + super(msg) + json = json + end + end + +end diff --git a/devops-service/lib/expire_handler.rb b/devops-service/lib/expire_handler.rb new file mode 100644 index 0000000..3a00ea0 --- /dev/null +++ b/devops-service/lib/expire_handler.rb @@ -0,0 +1,45 @@ +require "commands/server" +require 'rufus-scheduler' + +class ExpireHandler + include ServerCommands + + @@scheduler = Rufus::Scheduler.start_new + + def self.add server + unless server.expires_at.nil? + if server.expires_at < Time.now.to_i + ExpireHandler.do_it server + else + time = Time.at(server.expires_at) + @@scheduler.at time, ExpireHandler.new(server, chef_env) + DevopsLogger.logger "Server '#{server.chef_node_name}' will be expired at '#{time}'" + end + end + end + + def self.init + Devops::Db.connector.servers_find({"expires_at" => {"$ne" => nil}}, nil).each do |s| + ExpireHandler.add s + end + end + + def initialize server + @server = server + end + + def call(job) + ExpireHandler.do_it @server + end + + def self.do_it server + logger = DevopsLogger.logger + logger.info("Removing node '#{server.chef_node_name}' form project '#{server.project}' and env '#{server.deploy_env}'") + begin + delete_server(server, DevopsService.mongo, logger) + rescue => e + logger.error "ExpiredHandler error: " + e.message + end + end +end + diff --git a/devops-service/lib/project/handler/project_type.rb b/devops-service/lib/project/handler/project_type.rb deleted file mode 100644 index af51ced..0000000 --- a/devops-service/lib/project/handler/project_type.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Devops - module ProjectType - - class << self - @@types = {} - def included mod - @@types[mod.name] = mod - puts "Project type '#{mod.name}' registered" - end - - def types - @@types - end - end - - def prepare project - - end - - end -end diff --git a/devops-service/lib/project/handler/generic_type.rb b/devops-service/lib/project/type/generic_type.rb similarity index 100% rename from devops-service/lib/project/handler/generic_type.rb rename to devops-service/lib/project/type/generic_type.rb diff --git a/devops-service/lib/project/type/project_type.rb b/devops-service/lib/project/type/project_type.rb new file mode 100644 index 0000000..ffab1a7 --- /dev/null +++ b/devops-service/lib/project/type/project_type.rb @@ -0,0 +1,71 @@ +module Devops + module ProjectType + + class << self + @@types = {} + def included mod + @@types[mod.name] = mod + puts "Project type '#{mod.name}' registered" + end + + def types + @@types + end + end + + def prepare project + + end + + def extract_servers project, env, params +=begin + mongo = ::Devops::Db.connector + env_name = env.identifier + project_name = project.id + servers_info = [] + i = mongo.image env.image + rl = Set.new + rl.merge(project.run_list).merge(env.run_list) + o = { + :image => i, + :name => , + :flavor => flavor["id"], + :groups => params["groups"] || env.groups, + :run_list => rl, + :subnets => env.subnets, + :key => + } + servers_info.push(o) + + servers = [] + servers_info.each do |info| + servers.push s + end + return servers + + image = info[:image] +=end + s = Devops::Model::Server.new + s.provider = provider.name + s.project = project_name + s.deploy_env = env_name + s.run_list = params["run_list"] || [] + s.remote_user = image.remote_user + s.chef_node_name = params["name"] + s.key = params["key"] || provider.ssh_key +=begin + s.options = { + :image => image.id, + :flavor => info[:flavor], + :name => info[:name], + :groups => info[:groups], + :run_list => info[:run_list].merge(s.run_list), + :bootstrap_template => image.bootstrap_template, + :subnets => info[:subnets] + } +=end + #s.created_by = user + [s] + end + end +end diff --git a/devops-service/lib/project/handler/types_factory.rb b/devops-service/lib/project/type/types_factory.rb similarity index 100% rename from devops-service/lib/project/handler/types_factory.rb rename to devops-service/lib/project/type/types_factory.rb diff --git a/devops-service/providers/base_provider.rb b/devops-service/providers/base_provider.rb index 8e0195c..b2fd75d 100644 --- a/devops-service/providers/base_provider.rb +++ b/devops-service/providers/base_provider.rb @@ -23,5 +23,11 @@ module Provider param.nil? or param.empty? end + def set_tags instance_id, tags + end + + def unset_tags instance_id, tags + end + end end diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 17464bb..3c9455f 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -1,3 +1,4 @@ +require "exceptions/conflict_exception" require "providers/base_provider" module Provider @@ -90,28 +91,28 @@ module Provider convert_server list[0]["instancesSet"][0] end - def create_server s, out + def create_server s, image, flavor, subnets, groups, out out << "Creating server for project '#{s.project} - #{s.deploy_env}'\n" options = { - "InstanceType" => s.options[:flavor], - "Placement.AvailabilityZone" => s.options[:availability_zone], + "InstanceType" => flavor, +# "Placement.AvailabilityZone" => s.options[:availability_zone], "KeyName" => self.ssh_key } vpcId = nil - unless s.options[:subnets].empty? - options["SubnetId"] = s.options[:subnets][0] + unless subnets.empty? + options["SubnetId"] = subnets[0] vpcId = self.networks.detect{|n| n["name"] == options["SubnetId"]}["vpcId"] if vpcId.nil? out << "Can not get 'vpcId' by subnet name '#{options["SubnetId"]}'\n" return false end end - options["SecurityGroupId"] = extract_group_ids(s.options[:groups], vpcId).join(",") + options["SecurityGroupId"] = extract_group_ids(groups, vpcId).join(",") aws_server = nil compute = self.compute begin - aws_server = compute.run_instances(s.options[:image], 1, 1, options) + aws_server = compute.run_instances(image, 1, 1, options) rescue Excon::Errors::Unauthorized => ue #root = XML::Parser.string(ue.response.body).parse.root #msg = root.children.find { |node| node.name == "Message" } @@ -129,8 +130,6 @@ module Provider instance = abody["instancesSet"][0] s.id = instance["instanceId"] - out << "\nInstance Name: #{s.chef_node_name}" - out << "\nInstance ID: #{s.id}\n" out << "\nWaiting for server..." details, state = nil, instance["instanceState"]["name"] @@ -144,7 +143,7 @@ module Provider end s.public_ip = details["ipAddress"] s.private_ip = details["privateIpAddress"] - compute.create_tags(s.id, {"Name" => s.chef_node_name}) + set_tags(s.id, {"Name" => s.chef_node_name}) out << "\nDone\n\n" out << s.info @@ -183,6 +182,16 @@ module Provider end end + def set_tags instance_id, tags + raise ConflictException.new("You can not change 'Name' tag") if tags.key?("Name") + compute.create_tags(instance_id, tags) + end + + def unset_tags instance_id, tags + raise ConflictException.new("You can not change 'Name' tag") if tags.key?("Name") + compute.delete_tags(instance_id, tags) + end + def compute connection_compute(connection_options) end diff --git a/devops-service/providers/openstack.rb b/devops-service/providers/openstack.rb index 95fd1f5..c8844cc 100644 --- a/devops-service/providers/openstack.rb +++ b/devops-service/providers/openstack.rb @@ -1,3 +1,4 @@ +require "chef/json_compat" require "providers/base_provider" module Provider @@ -90,29 +91,34 @@ module Provider end end - def create_server s, out + def create_server s, image, flavor, subnets, groups, out out << "Creating server for project '#{s.project} - #{s.deploy_env}'\n" - networks = self.networks.select{|n| s.options[:subnets].include?(n["name"])} - buf = s.options[:subnets] - networks.map{|n| n["name"]} + if s.chef_node_name.nil? + out << "Generate new instance name: " + out << s.chef_node_name = create_default_chef_node_name(s) + out << "\n" + end + networks = self.networks.select{|n| subnets.include?(n["name"])} + buf = subnets - networks.map{|n| n["name"]} unless buf.empty? out << "No networks with names '#{buf.join("', '")}' found" return false end - s.options[:flavor] = self.compute.list_flavors_detail.body["flavors"].detect{|f| f["name"] == s.options[:flavor]}["id"] - out << "Creating server with name '#{s.chef_node_name}', image '#{s.options[:image]}', flavor '#{s.options[:flavor]}', key '#{s.key}' and networks '#{networks.map{|n| n["name"]}.join("', '")}'...\n\n" + flavor = self.compute.list_flavors_detail.body["flavors"].detect{|f| f["name"] == flavor}["id"] + out << "Creating server with name '#{s.chef_node_name}', image '#{image}', flavor '#{flavor}', key '#{s.key}' and networks '#{networks.map{|n| n["name"]}.join("', '")}'...\n\n" compute = self.compute begin - o_server = compute.create_server(s.chef_node_name, s.options[:image], s.options[:flavor], + o_server = compute.create_server(s.chef_node_name, image, flavor, "nics" => networks.map{|n| {"net_id" => n["id"]}}, - "security_groups" => s.options[:groups], + "security_groups" => groups, "key_name" => s.key) rescue Excon::Errors::BadRequest => e response = ::Chef::JSONCompat.from_json(e.response.body) if response['badRequest']['code'] == 400 if response['badRequest']['message'] =~ /Invalid flavorRef/ - out << "\nERROR: Bad request (400): Invalid flavor id specified: #{s.options[:flavor]}" + out << "\nERROR: Bad request (400): Invalid flavor id specified: #{flavor}" elsif response['badRequest']['message'] =~ /Invalid imageRef/ - out << "\nERROR: Bad request (400): Invalid image specified: #{s.options[:image]}" + out << "\nERROR: Bad request (400): Invalid image specified: #{image}" else out << "\nERROR: Bad request (400): #{response['badRequest']['message']}" end @@ -135,8 +141,6 @@ module Provider sbody = o_server.body s.id = sbody["server"]["id"] - out << "\nInstance Name: #{s.chef_node_name}" - out << "\nInstance ID: #{s.id}\n" out << "\nWaiting for server..." details, status = nil, nil @@ -145,13 +149,14 @@ module Provider details = compute.get_server_details(s.id).body status = details["server"]["status"].upcase if status == "ERROR" - out << "Server returns status 'ERROR'" + out << " error\nServer returns status 'ERROR'" + out << details["server"] return false end end network = networks[0]["name"] s.private_ip = details["server"]["addresses"][network][0]["addr"] - out << "\nDone\n\n" + out << " done\n\n" out << s.info true end diff --git a/devops-service/providers/static.rb b/devops-service/providers/static.rb index 31bd748..9de6570 100644 --- a/devops-service/providers/static.rb +++ b/devops-service/providers/static.rb @@ -47,7 +47,7 @@ module Provider "static-#{s.project}-#{s.deploy_env}-#{Time.now.to_i}" end - def create_server s, out + def create_server s, image, flavor, subnets, groups, out out << "Unsupported operation: ca not create server for provider 'static'" false end diff --git a/devops-service/sinatra/methods_with_headers.rb b/devops-service/sinatra/methods_with_headers.rb index aeafdf9..2bce5f1 100644 --- a/devops-service/sinatra/methods_with_headers.rb +++ b/devops-service/sinatra/methods_with_headers.rb @@ -6,7 +6,6 @@ module Sinatra class Base class << self - # TODO: add protect! method def get_with_headers path, opt={}, &block headers = opt.delete(:headers) || [] before path do @@ -48,6 +47,20 @@ module Sinatra end end + def patch_with_headers path, opt={}, &block + headers = opt.delete(:headers) || [] + before path do + check_headers *headers + protect! + end + + patch path, opt, &block + + after path do + statistic + end + end + def delete_with_headers path, opt={}, &block headers = opt.delete(:headers) || [] before path do diff --git a/devops-service/workers/bootstrap_worker.rb b/devops-service/workers/bootstrap_worker.rb index 596377b..8549d05 100644 --- a/devops-service/workers/bootstrap_worker.rb +++ b/devops-service/workers/bootstrap_worker.rb @@ -12,7 +12,8 @@ class BootstrapWorker < Worker include ServerCommands def perform(dir, e_provider, server, owner, conf) - call(conf, e_provider, dir) do |mongo, provider, out, file| + call(conf, e_provider, dir) do |provider, out, file| + mongo = Devops::Db.connector s = Server.new(server) s.options = convert_config(server["options"]) o = { @@ -27,12 +28,8 @@ class BootstrapWorker < Worker key = mongo.key(s.key) out << "\nBootstrap with run list: #{s.options[:run_list].inspect}" - status = bootstrap(s, out, key.path, logger) - if status == 0 - out << "Chef node name: #{s.chef_node_name}\n" - mongo.server_set_chef_node_name s - out << "Chef node name has been updated\n" - end + status = bootstrap(s, out, key.path) + mongo.set_report_server_data(jid, s.chef_node_name, s.public_ip || s.private_ip) status end end diff --git a/devops-service/workers/create_server_worker.rb b/devops-service/workers/create_server_worker.rb index f8b4c6b..93a56d8 100644 --- a/devops-service/workers/create_server_worker.rb +++ b/devops-service/workers/create_server_worker.rb @@ -11,21 +11,25 @@ require "db/mongo/models/report" class CreateServerWorker < Worker include ServerCommands - def perform(dir, e_provider, server, owner, conf) - call(conf, e_provider, dir) do |mongo, provider, out, file| - s = Server.new(server) - s.options = convert_config(server["options"]) + def perform(dir, params, owner, conf) + logger.info "Create server" + call(conf, nil, dir) do |provider, out, file| + mongo = Devops::Db.connector + project = mongo.project(params["project"]) + env = project.deploy_env(params["deploy_env"]) +# s = Devops::Model::Server.new(server) +# s.options = convert_config(server["options"]) o = { "file" => file, "_id" => jid, "created_by" => owner, - "project" => s.project, - "deploy_env" => s.deploy_env, - "type" => Report::SERVER_TYPE + "project" => project.id, + "deploy_env" => env.identifier, + "type" => Devops::Model::Report::SERVER_TYPE } - mongo.save_report(Report.new(o)) + mongo.save_report(Devops::Model::Report.new(o)) - status = create_server_proc.call(out, s, provider, mongo) + status = create_server(project, env, params, owner, out) status end end diff --git a/devops-service/workers/deploy_worker.rb b/devops-service/workers/deploy_worker.rb index 73973a7..a5b70d3 100644 --- a/devops-service/workers/deploy_worker.rb +++ b/devops-service/workers/deploy_worker.rb @@ -1,6 +1,3 @@ -#root = File.join(File.dirname(__FILE__), "..") -#$LOAD_PATH.push root unless $LOAD_PATH.include? root - require File.join(File.dirname(__FILE__), "worker") require "commands/deploy" @@ -10,8 +7,9 @@ require "db/mongo/models/report" class DeployWorker < Worker include DeployCommands - def perform(dir, server, tags, owner, conf) - call(conf, nil, dir) do |mongo, provider, out, file| + def perform(dir, server, tags, owner, conf, deploy_info) + call(conf, nil, dir) do |provider, out, file| + mongo = Devops::Db.connector s = Server.new(server) o = { "file" => file, @@ -19,11 +17,14 @@ class DeployWorker < Worker "created_by" => owner, "project" => s.project, "deploy_env" => s.deploy_env, - "type" => Report::DEPLOY_TYPE + "type" => Report::DEPLOY_TYPE, + "status" => STATUS::RUNNING, + "chef_node_name" => s.chef_node_name, + "host" => s.public_ip || s.private_ip } mongo.save_report(Report.new(o)) - status = deploy_server_proc.call(out, s, mongo, tags) + status = deploy_server_proc.call(out, s, tags, deploy_info) status end end diff --git a/devops-service/workers/project_test_worker.rb b/devops-service/workers/project_test_worker.rb index 3b951f9..bca1cb0 100644 --- a/devops-service/workers/project_test_worker.rb +++ b/devops-service/workers/project_test_worker.rb @@ -13,8 +13,9 @@ class ProjectTestWorker < Worker include StatusCommands def perform(dir, params, conf) - call(conf, nil, dir) do |mongo, provider, out, file| - logger.info "Test project '#{params["project"]}' and env '#{params["env"]}' (user - #{params["user"]})" + call(conf, nil, dir) do |provider, out, file| + DevopsLogger.logger.info "Test project '#{params["project"]}' and env '#{params["env"]}' (user - #{params["user"]})" + mongo = Devops::Db.connector project = mongo.project(params["project"]) env = project.deploy_env(params["env"]) user = params["user"] @@ -29,7 +30,7 @@ class ProjectTestWorker < Worker mongo.save_report(Report.new(o)) provider = ::Provider::ProviderFactory.get(env.provider) - servers = extract_servers(provider, project, env, {}, user, mongo) + servers = extract_servers(provider, project, env, {}, user) result = {:servers => []} project.deploy_envs = [ env ] result[:project] = project.to_hash @@ -51,7 +52,7 @@ class ProjectTestWorker < Worker key = mongo.key(s.key) out << "\n=== Bootstrap ===\n" out.flush - r = bootstrap(s, out, key.path, logger) + r = bootstrap(s, out, key.path) t1 = Time.now sr[:chef_node_name] = s.chef_node_name if r == 0 diff --git a/devops-service/workers/stack_bootstrap_worker.rb b/devops-service/workers/stack_bootstrap_worker.rb index 95d9b68..7bc2be3 100644 --- a/devops-service/workers/stack_bootstrap_worker.rb +++ b/devops-service/workers/stack_bootstrap_worker.rb @@ -3,7 +3,6 @@ require "commands/stack" require "db/mongo/models/stack/stack_factory" require "db/mongo/models/project" require "db/mongo/models/report" -require 'workers/workers_storage' class StackBootstrapWorker < Worker include StackCommands @@ -12,7 +11,8 @@ class StackBootstrapWorker < Worker # :provider # :stack_id def perform(options) - call(options['config'], options['provider'], options['dir']) do |mongo, provider, out, file| + call(options['config'], options['provider'], options['dir']) do |provider, out, file| + mongo = Devops::Db.connector stack = mongo.stack(options['stack_id']) save_report(mongo, stack, file) diff --git a/devops-service/workers/worker.rb b/devops-service/workers/worker.rb index f5d6576..251c41a 100644 --- a/devops-service/workers/worker.rb +++ b/devops-service/workers/worker.rb @@ -6,8 +6,11 @@ require "sidekiq/api" require "fileutils" -require "db/mongo/mongo_connector" +require "core/devops-config" +require "core/devops-logger" +require "core/devops-db" require "providers/provider_factory" +require 'workers/workers_storage' class Worker include Sidekiq::Worker @@ -23,16 +26,10 @@ class Worker def convert_config conf config = {} conf.each {|k,v| config[k.is_a?(String) ? k.to_sym : k] = v} - logger.debug "Config: #{config.inspect}" + DevopsLogger.logger.debug "Config: #{config.inspect}" config end - def mongo_connector config - mongo = MongoConnector.new(config[:mongo_db], config[:mongo_host], config[:mongo_port], config[:mongo_user], config[:mongo_password]) - logger.debug "Mongo connector: #{mongo.inspect}" - mongo - end - def set_status id, status Sidekiq.redis {|con| con.hset "devops", id, status} end @@ -45,27 +42,30 @@ class Worker end def call conf, e_provider, dir + DevopsLogger.logger = logger FileUtils.mkdir_p(dir) unless File.exists?(dir) set_status jid, "init" - config = convert_config(conf) + DevopsConfig.config = convert_config(conf) file = File.join(dir, jid) error = nil - mongo = nil provider = nil begin - mongo = mongo_connector(config) + Devops::Db.init + ::Provider::ProviderFactory.init(DevopsConfig.config) unless e_provider.nil? - ::Provider::ProviderFactory.init(config) provider = ::Provider::ProviderFactory.get(e_provider) end rescue Exception => e error = e + DevopsLogger.logger.error e.message + return end + mongo = ::Devops::Db.connector File.open(file, "w") do |out| begin set_status jid, STATUS::RUNNING raise error unless error.nil? - status = yield(mongo, provider, out, file) + status = yield(provider, out, file) status = (status == 0 ? STATUS::COMPLETED : STATUS::FAILED) set_status jid, status mongo.set_report_status(jid, status)