diff --git a/devops-service/app/api2/handlers/bootstrap_templates.rb b/devops-service/app/api2/handlers/bootstrap_templates.rb new file mode 100644 index 0000000..94ffa6b --- /dev/null +++ b/devops-service/app/api2/handlers/bootstrap_templates.rb @@ -0,0 +1,14 @@ +require "commands/bootstrap_templates" + +module Devops + module API2_0 + module Handler + class BootstrapTemplates + + include BootstrapTemplatesCommands + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/deploy.rb b/devops-service/app/api2/handlers/deploy.rb new file mode 100644 index 0000000..491b564 --- /dev/null +++ b/devops-service/app/api2/handlers/deploy.rb @@ -0,0 +1,61 @@ +require "commands/deploy" +require "commands/status" +require "workers/deploy_worker" + +module Devops + module API2_0 + module Handler + class Deploy + extend DeployCommands + extend StatusCommands + + def initialize req, params + @request = req + @params = params + end + + def deploy names, tags + dir = DevopsConfig.config[:report_dir_v2] + files = [] + uri = URI.parse(@request.url) + servers(names).each do |s| + project = begin + Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + rescue InvalidPrivileges, RecordNotFound => e + next + end + jid = DeployWorker.perform_async(dir, s.to_hash, tags, @request.env['REMOTE_USER'], DevopsConfig.config) + files.push jid + end + files + end + + def deploy_stream out, names, tags + status = [] + servers(names).each do |s| + project = begin + Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + rescue InvalidPrivileges, RecordNotFound => e + out << e.message + "\n" + status.push 2 + next + end + res = deploy_server_proc.call(out, s, tags) + status.push(res) + end + out << create_status(status) + rescue RecordNotFound => e + out << e.message + end + + def servers names + servers = Devops::Db.connector.servers(nil, nil, names, true) + raise RecordNotFound.new("No reserved servers found for names '#{names.join("', '")}'") if servers.empty? + servers.sort_by!{|s| names.index(s.chef_node_name)} + servers + end + end + end + end +end + diff --git a/devops-service/app/api2/handlers/filter.rb b/devops-service/app/api2/handlers/filter.rb new file mode 100644 index 0000000..8a4cf23 --- /dev/null +++ b/devops-service/app/api2/handlers/filter.rb @@ -0,0 +1,24 @@ +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Filter < RequestHandler + + def available_images provider + Devops::Db.connector.available_images(provider) + end + + def add_images images, provider + Devops::Db.connector.add_available_images(images, provider) + end + + def delete_images images, provider + Devops::Db.connector.delete_available_images(images, provider) + end + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/flavor.rb b/devops-service/app/api2/handlers/flavor.rb new file mode 100644 index 0000000..7c36091 --- /dev/null +++ b/devops-service/app/api2/handlers/flavor.rb @@ -0,0 +1,22 @@ +require_relative "request_handler" +require "providers/provider_factory" + +module Devops + module API2_0 + module Handler + class Flavor < RequestHandler + + def initialize request, params + @provider = params[:provider] + end + + def flavors + p = ::Provider::ProviderFactory.get @provider + p.flavors + end + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/group.rb b/devops-service/app/api2/handlers/group.rb new file mode 100644 index 0000000..2016d88 --- /dev/null +++ b/devops-service/app/api2/handlers/group.rb @@ -0,0 +1,20 @@ +require "providers/provider_factory" + +module Devops + module API2_0 + module Handler + class Group + + def initialize provider + @provider = provider + end + + def groups params + p = ::Provider::ProviderFactory.get @provider + p.groups(params) + end + end + end + end +end + diff --git a/devops-service/app/api2/handlers/image.rb b/devops-service/app/api2/handlers/image.rb new file mode 100644 index 0000000..08278e8 --- /dev/null +++ b/devops-service/app/api2/handlers/image.rb @@ -0,0 +1,51 @@ +require "providers/provider_factory" +require "commands/image" +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Image < RequestHandler + + extend ImageCommands + + def images provider + Devops::Db.connector.images(provider) + end + + def provider_images provider + Image.get_available_provider_images(Devops::Db.connector, provider) + end + + def get_image id + Devops::Db.connector.image(id) + end + + def create_image image + Devops::Db.connector.image_insert Devops::Model::Image.new(image) + end + + def update_image id, image + Devops::Db.connector.image id + obj = Devops::Model::Image.new(image) + obj.id = id + Devops::Db.connector.image_update image + end + + def delete_image id + projects = Devops::Db.connector.projects_by_image id + unless projects.empty? + ar = [] + projects.each do |p| + ar += p.deploy_envs.select{|e| e.respond_to?(:image)}.select{|e| e.image == id}.map{|e| "#{p.id}.#{e.identifier}"} + end + raise DependencyError.new "Deleting is forbidden: Image is used in #{ar.join(", ")}" + end + + Devops::Db.connector.image_delete id + end + end + end + end +end + diff --git a/devops-service/app/api2/handlers/key.rb b/devops-service/app/api2/handlers/key.rb new file mode 100644 index 0000000..338a2d0 --- /dev/null +++ b/devops-service/app/api2/handlers/key.rb @@ -0,0 +1,45 @@ +require "db/mongo/models/key" +require "fileutils" + +module Devops + module API2_0 + module Handler + class Key + + 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 + end + + def create body, file_name + File.open(file_name, "w") do |f| + f.write(body["content"]) + f.chmod(0400) + end + + key = Devops::Model::Key.new({"path" => file_name, "id" => body["key_name"]}) + Devops::DB.connector.key_insert key + end + + 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 + + k = mongo.key key_id + begin + FileUtils.rm(k.path) + rescue + logger.error "Missing key file for #{key_id} - #{k.filename}" + end + mongo.key_delete key_id + end + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/network.rb b/devops-service/app/api2/handlers/network.rb new file mode 100644 index 0000000..81964c3 --- /dev/null +++ b/devops-service/app/api2/handlers/network.rb @@ -0,0 +1,17 @@ +require "providers/provider_factory" +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Network < RequestHandler + + def networks provider + p = ::Provider::ProviderFactory.get provider + p.networks_detail + end + end + end + end +end + diff --git a/devops-service/app/api2/handlers/project.rb b/devops-service/app/api2/handlers/project.rb new file mode 100644 index 0000000..f4f5de1 --- /dev/null +++ b/devops-service/app/api2/handlers/project.rb @@ -0,0 +1,231 @@ +require "commands/deploy" +require "commands/status" +require "commands/server" +require "db/mongo/models/project" +require "workers/project_test_worker" +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Project < RequestHandler + + extend DeployCommands + extend StatusCommands + extend ServerCommands + + 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) + end + + def project id + Devops::Db.connector.project(id) + end + + def project_servers id + Devops::Db.connector.project(id) + Devops::Db.connector.servers(id, @params[:deploy_env]) + end + + # TODO: multi project + def create_project body + p = Devops::Model::Project.new(body) + halt_response("Project '#{p.id}' already exist") if Devops::Db.connector.is_project_exists?(p) + p.add_authorized_user [@request.env['REMOTE_USER']] + p.create + if p.multi? + "Project '#{p.id}' with type 'multi' created" + else + roles = create_roles p.id, p.deploy_envs, logger + "Project '#{p.id}' created. " + create_roles_response(roles) + end + end + + # TODO: multi project + def update_project id, body + project = Devops::Model::Project.new(body) + project.id = id + old_project = Devops::Db.connector.project id + Devops::Db.connector.project_update project + roles = create_new_roles(old_project, project, logger) + create_roles_response(roles) + end + + # TODO: multi project + def update_project_users id, deploy_env, users + project = Devops::Db.connector.project(id) + dbusers = Devops::Db.connector.users(users).map{|u| u.id} + buf = dbusers - users + project.add_authorized_user users, deploy_env + Devops::Db.connector.project_update(project) + info = "Users '#{dbusers.join("', '")}' has been added to '#{id}' project's authorized users" + info << ", invalid users: '#{buf.join("', '")}'" unless buf.empty? + info + end + + # TODO: multi project + def delete_project_users id, deploy_env, 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 + 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}'" + end + + def delete_project id, deploy_env + 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) + info = if deploy_env.nil? + project.delete + "Project '#{id}' is deleted" + else + project.remove_env deploy_env + Devops::Db.connector.project_update project + "Project '#{id}'. Deploy environment '#{deploy_env}' has been deleted" + end + end + + def deploy_project_stream out, id, deploy_env, servers, body + 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") + status = [] + servers.each do |s| + begin + Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + rescue InvalidPrivileges, RecordNotFound => e + out << e.message + "\n" + status.push 2 + next + end + unless keys.key? s.key + k = Devops::Db.connector.key s.key + keys[s.key] = k.path + end + status.push(deploy_server(out, s, keys[s.key])) + end + status + end + + def deploy_project id, deploy_env, servers, body + dir = DevopsConfig[:report_dir_v2] + files = [] + uri = URI.parse(request.url) + servers(id, deploy_env, servers).each do |s| + project = begin + Devops::Db.connector.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] + rescue InvalidPrivileges, RecordNotFound => e + next + end + jid = DeployWorker.perform_async(dir, s.to_hash, [], DevopsConfig.config) + #logger.info "Job '#{jid}' has been started" + uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid + files.push uri.to_s + end + files + end + + def servers project_id, deploy_env, servers + project = Devops::Db.connector.project(project_id) + dbservers = Devops::Db.connector.servers(project_id, deploy_env, servers, true) + end + + def archive_project id + project = Devops::Db.connector.project(id) + Devops::Db.connector.archive_project(id) + "Project '#{id}' has been archived" + end + + def unarchive_project id + project = Devops::Db.connector.project(id) + Devops::Db.connector.unarchive_project(id) + "Project '#{id}' has been unarchived" + end + + 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}'" + if env.provider == ::Provider::Static::PROVIDER + msg = "Can not test environment with provider '#{::Provider::Static::PROVIDER}'" + Logger.warn msg + raise InvalidRecord.new(msg) + end + + dir = DevopsConfig[:report_dir_v2] + uri = URI.parse(request.url) + p = { + :project => project.id, + :env => env.identifier, + :user => @request.env['REMOTE_USER'] + } + jid = ProjectTestWorker.perform_async(dir, p, DevopsConfig.config) + Worker.set_status jid, Worker::STATUS::IN_QUEUE + #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 + all_roles = KnifeCommands.roles + return " Can't get roles list" if all_roles.nil? + roles = {:new => [], :error => [], :exist => []} + envs.each do |e| + role_name = KnifeCommands.role_name(project_id, e.identifier) + begin + if all_roles.include? role_name + roles[:exist].push role_name + else + KnifeCommands.create_role role_name, project_id, e.identifier + roles[:new].push role_name + 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}" + end + end + roles + end + + def create_new_roles old_project, new_project, logger + old_project.deploy_envs.each do |e| + new_project.remove_env(e.identifier) + end + create_roles new_project.id, new_project.deploy_envs, logger + end + + def create_roles_response roles + if roles.is_a?(String) + roles + else + info = "" + info += " Project roles '#{roles[:new].join("', '")}' have been automaticaly created" unless roles[:new].empty? + info += " Project roles '#{roles[:exist].join("', '")}' weren't created because they exist" unless roles[:exist].empty? + info += " Project roles '#{roles[:error].join("', '")}' weren't created because of internal error" unless roles[:error].empty? + info + end + end + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/provider.rb b/devops-service/app/api2/handlers/provider.rb new file mode 100644 index 0000000..51f1167 --- /dev/null +++ b/devops-service/app/api2/handlers/provider.rb @@ -0,0 +1,16 @@ +require "providers/provider_factory" +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Provider < RequestHandler + + def providers + ::Provider::ProviderFactory.providers + end + + end + end + end +end diff --git a/devops-service/app/api2/handlers/report.rb b/devops-service/app/api2/handlers/report.rb new file mode 100644 index 0000000..1918d66 --- /dev/null +++ b/devops-service/app/api2/handlers/report.rb @@ -0,0 +1,64 @@ +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Report < RequestHandler + + def initialize request, params + @params = params + end + + def options + 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] + end + options + end + + def all + Devops::Db.connector.reports(options) + end + + def all_latest + Devops::Db.connector.latest_reports(options()) + end + + def attributes name + Devops::Db.connector.reports_attributes_values(name) + end + + def report id + r = Devops::Db.connector.report(id) + file = r.file + raise RecordNotFound.new("Report '#{id}' does not exist") unless File.exists? file + return Rack::Utils.escape_html(File.read(file)), completed?(id) + rescue RecordNotFound => e + if status(id) == Worker::STATUS::IN_QUEUE + return "Task '#{id}' has been queued", false + else + raise e + end + end + + def status id + Sidekiq.redis do |connection| + connection.hget("devops", id) + end + end + + def completed? id + r = self.status(id) + r == "completed" or r == "failed" + end + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/request_handler.rb b/devops-service/app/api2/handlers/request_handler.rb new file mode 100644 index 0000000..8a6d70b --- /dev/null +++ b/devops-service/app/api2/handlers/request_handler.rb @@ -0,0 +1,12 @@ +module Devops + module API2_0 + module Handler + class RequestHandler + def initialize request, params + @request = request + @params = params + end + end + end + end +end diff --git a/devops-service/app/api2/handlers/script.rb b/devops-service/app/api2/handlers/script.rb new file mode 100644 index 0000000..2d58401 --- /dev/null +++ b/devops-service/app/api2/handlers/script.rb @@ -0,0 +1,98 @@ +require "providers/provider_factory" +require "fileutils" +require "commands/status" +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Script < RequestHandler + + 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'] + 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 + addr = "#{s.remote_user}@#{s.public_ip || s.private_ip}" + ssh_cmd = "ssh -i %s #{addr} '#{cmd}'" + out << ssh_cmd % File.basename(cert.path) + out << "\n" + IO.popen((ssh_cmd % cert.path) + " 2>&1") do |so| + while line = so.gets do + out << line + end + end + 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 + 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 + 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" + + begin + IO.popen( (ssh_cmd % [file]) + " 2>&1") do |so| + while line = so.gets do + out << line + end + so.close + status.push $?.to_i + end + rescue IOError => e + logger.error e.message + out << e.message + status.push 3 + end + end + out << create_status(status) + end + + def create_script file_name + file = File.join(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)} + end + + def delete_script file_name + file = File.join(settings.scripts_dir, file_nsme) + raise RecordNotFound.new("File '#{file_name}' does not exist", 404) unless File.exists?(file) + FileUtils.rm(file) + end + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/server.rb b/devops-service/app/api2/handlers/server.rb new file mode 100644 index 0000000..202d68b --- /dev/null +++ b/devops-service/app/api2/handlers/server.rb @@ -0,0 +1,269 @@ +require 'rufus-scheduler' +require "uri" + +require "commands/status" +require "commands/server" +require "commands/bootstrap_templates" +require "commands/knife_commands" + +require "providers/provider_factory" + +require "db/mongo/models/server" + +require "workers/create_server_worker" +require "workers/bootstrap_worker" + +module Devops + module API2_0 + module Handler + class Server + + extend StatusCommands + extend ServerCommands + extend BootstrapTemplatesCommands + + scheduler = Rufus::Scheduler.new + + def servers fields, reserved + Devops::DB.connector.servers(nil, nil, nil, reserved, fields).map {|s| s.to_hash} + end + + def chef_servers + KnifeCommands.chef_node_list + end + + def provider_servers provider + ::Provider::ProviderFactory.get(provider).servers + end + + def server id + get_server_by_key(id, @params[:key]) + end + + def delete id, key + s = get_server_by_key(params[:id], key) + ### Authorization + Devops::Db.connector.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] + delete_server(s, Devops::Db.connector, 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) + status.push res + end + status + end + + def create_server body + 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) + Worker.set_status jid, Worker::STATUS::IN_QUEUE + #logger.info "Job '#{jid}' has been started" + uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid + files.push uri.to_s + end + sleep 1 + files + end + + def prepare_create_server body + user = @request.env['REMOTE_USER'] + key_name = body["key"] + new_key = Devops::Db.connector.key(key_name) unless key_name.nil? + + p = Devops::Db.connector.check_project_auth(body["project"], body["deploy_env"], user) + env = p.deploy_env(body["deploy_env"]) + + provider = ::Provider::ProviderFactory.get(env.provider) + server_name = body["name"] + check_chef_node_name(server_name, provider) unless server_name.nil? + groups = body["groups"] + unless groups.nil? + buf = groups - provider.groups.keys + halt_response("Invalid security groups '#{buf.join("', '")}' for provider '#{provider.name}'") if buf.empty? + end + + Server.extract_servers(provider, p, env, body, user, Devops::Db.connector) + end + + def pause_server node_name, key + s = Server.get_server_by_key(node_name, key) + ## Authorization + Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + provider = ::Provider::ProviderFactory.get(s.provider) + r = provider.pause_server s + if r.nil? + "Server with instance ID '#{s.id}' and node name '#{node_name}' is paused" + else + raise ConflictException.new("Server with instance ID '#{s.id}' and node name '#{node_name}' can not be paused, It in state '#{r}'", 409) + end + end + + def unpause_server node_name, key + s = Server.get_server_by_key(params[:node_name], key) + ## Authorization + Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER'] + provider = ::Provider::ProviderFactory.get(s.provider) + r = provider.unpause_server s + if r.nil? + "Server with instance ID '#{s.id}' and node name '#{node_name}' is unpaused" + else + raise ConflictException.new("Server with instance ID '#{s.id}' and node name '#{node_name}' can not be unpaused, It in state '#{r}'") + end + end + + def reserve_server node_name, key + s = get_server_by_key(node_name, key) + Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_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'] + 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 + status = [] + cert = Devops::Db.connector.key s.key + logger.debug "Bootstrap certificate path: #{cert.path}" + bootstrap s, out, cert.path, logger + str = nil + r = if check_server(s) + Devops::Db.connector.server_set_chef_node_name s + str = "Server with id '#{s.id}' is bootstraped" + logger.info str + 0 + else + str = "Server with id '#{s.id}' is not bootstraped" + logger.warn str + 1 + end + status.push r + out << str + out << "\n" + status + end + + def bootstrap_server body + s = prepare_create_server body + 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) + Worker.set_status jid, Worker::STATUS::IN_QUEUE + logger.info "Job '#{jid}' has been started" + uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid + uri.query = nil + uri.fragment = nil + files.push uri.to_s + sleep 1 + files + end + + def prepare_bootstrap_server body + 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'] + d = p.deploy_env s.deploy_env + + provider = ::Provider::ProviderFactory.get(s.provider) + + check_chef_node_name(name, provider) unless name.nil? + s.options = { + :run_list => rl || d.run_list, + } + unless t.nil? + templates = get_templates + halt_response("Invalid bootstrap template '#{t}', available values: #{templates.join(", ")}", 400) unless templates.include?(t) + s.options[:bootstrap_template] = t + end + s.chef_node_name = name || provider.create_default_chef_node_name(s) + s + end + + def add_server body + project = body["project"] + deploy_env = body["deploy_env"] + p = Devops::Db.connector.check_project_auth project, deploy_env, @request.env['REMOTE_USER'] + + d = p.deploy_env(deploy_env) + + cert = Devops::Db.connector.key(body["key"]) + provider = ::Provider::ProviderFactory.get("static") + s = Devops::Model::Server.new + s.provider = provider.name + s.project = project + s.deploy_env = deploy_env + s.remote_user = body["remote_user"] + s.private_ip = body["private_ip"] + s.public_ip = body["public_ip"] + s.static = true + s.id = "static_#{cert.id}-#{Time.now.to_i}" + s.key = cert.id + Devops::Db.connector.server_insert s + "Server '#{s.id}' has been added" + end + + def get_server_by_key id, key + mongo = Devops::Db.connector + key == "instance" ? mongo.server_by_instance_id(id) : mongo.server_by_chef_node_name(id) + end + + def check_chef_node_name name, provider + mongo = Devops::Db.connector + mongo.server_by_chef_node_name name + halt(400, "Server with name '#{name}' already exist") + rescue RecordNotFound => e + # server not found - OK + s = provider.servers.detect {|s| s["name"] == name} + halt(400, "#{provider.name} node with name '#{name}' already exist") unless s.nil? + s = KnifeCommands.chef_node_list.detect {|n| n == name} + halt(400, "Chef node with name '#{name}' already exist") unless s.nil? + s = KnifeCommands.chef_client_list.detect {|c| c == name} + halt(400, "Chef client with name '#{name}' already exist") unless s.nil? + end + + end + + class ExpireHandler + include ServerCommands + + def initialize server, logger + @server = server + @logger = logger + end + + def call(job) + @logger.info("Removing node '#{@server.chef_node_name}' form project '#{@server.project}' and env '#{@server.deploy_env}'") + begin + delete_server(@server, Devops::Db.connector, @logger) + rescue => e + logger.error "ExpiredHandler error: " + e.message + end + end + end + + end + end +end + diff --git a/devops-service/app/api2/handlers/stack.rb b/devops-service/app/api2/handlers/stack.rb new file mode 100644 index 0000000..58566d3 --- /dev/null +++ b/devops-service/app/api2/handlers/stack.rb @@ -0,0 +1,51 @@ +require 'db/mongo/models/stack/stack_factory' +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class Stack < RequestHandler + + def stacks + Devops::Db.connector.stacks + end + + def stacks_for_provider provider + Devops::Db.connector.stacks(provider) + end + + def create_stack object + stack_model = Model::StackFactory.create(object['provider'], object) + Devops::Db.connector.stack_insert(stack_model) + stack_model + end + + def stack id + Devops::Db.connector.stack(id) + end + + def delete_stack id + stack = self.stack(id) + stack.delete_stack_in_cloud! + Devops::Db.connector.stack_delete(id) + end + + def sync_details id + stack = self.stack(id) + stack.sync_details! + Devops::Db.connector.stack_update(stack) + end + + def resources id + stack = Devops::Db.connector.stack(id) + stack.resources + end + + def resources id, resource_id + stack = Devops::Db.connector.stack(id) + stack.resource(resource_id) + end + end + end + end +end diff --git a/devops-service/app/api2/handlers/stack_preset.rb b/devops-service/app/api2/handlers/stack_preset.rb new file mode 100644 index 0000000..160afc1 --- /dev/null +++ b/devops-service/app/api2/handlers/stack_preset.rb @@ -0,0 +1,28 @@ +require 'json' +require 'lib/stack_presets/factory' +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class StackPreset < RequestHandler + + def presets + Devops::StackPresetsFactory.list + end + + def preset id + Devops::StackPresetsFactory.get(id) + end + + def apply id, body + preset = Devops::StackPresetsFactory.get(id) + preset.create_stack_from_preset(body) + Devops::Db.connector.stack_insert(stack) + end + + end + end + end +end + diff --git a/devops-service/app/api2/handlers/stack_template.rb b/devops-service/app/api2/handlers/stack_template.rb new file mode 100644 index 0000000..93a0f6a --- /dev/null +++ b/devops-service/app/api2/handlers/stack_template.rb @@ -0,0 +1,34 @@ +require 'db/mongo/models/stack_template/stack_template_factory' +require_relative "request_handler" + +module Devops + module API2_0 + module Handler + class StackTemplate < RequestHandler + + def stack_templates + Devops::Db.connector.stack_templates + end + + def stack_templates_for_provider provider + Devops::Db.connector.stack_templates(provider) + end + + def create_stack_template body + template_model = Model::StackTemplateFactory.create(body['provider'], body) + Devops::Db.connector.stack_template_insert(template_model) + template_model + end + + def get_stack_template id + Devops::Db.connector.stack_template(id) + end + + def delete_stack_template id + Devops::Db.connector.stack_template_delete id + end + + end + end + end +end diff --git a/devops-service/app/api2/handlers/tag.rb b/devops-service/app/api2/handlers/tag.rb new file mode 100644 index 0000000..ea29315 --- /dev/null +++ b/devops-service/app/api2/handlers/tag.rb @@ -0,0 +1,34 @@ +require "commands/knife_commands" + +module Devops + module API2_0 + module Handler + class Tag + + def initialize node_name + @node_name = node_name + @server = Devops::DB.connector.server_by_chef_node_name(@node_name) + #TODO: raise + halt_response("No servers found for name '#{@node_name}'", 404) if @server.nil? + end + + def tags + KnifeCommands.tags_list(@node_name) + end + + def set_tags 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] + end + + def self.unset_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] + end + end + end + end +end + diff --git a/devops-service/app/api2/handlers/user.rb b/devops-service/app/api2/handlers/user.rb new file mode 100644 index 0000000..9da89ca --- /dev/null +++ b/devops-service/app/api2/handlers/user.rb @@ -0,0 +1,47 @@ +require "db/mongo/models/user" + +module Devops + module API2_0 + module Handler + class User + + def users + Devops::Db.connector.users + end + + def create body + Devops::Db.connector.user_insert Devops::Model::User.new(body) + end + + def delete user_id + Devops::Db.connector.user_delete user_id + end + + def change_user_privileges user_id, cmd, privileges + change_user(user_id) do + user.grant(cmd, privileges) + end + end + + def change_email user_id, val + change_user(user_id) do + user.email = val + end + end + + def change_password user_id, val + change_user(user_id) do + user.password = val + end + end + + def change_user user_id + user = Devops::Db.connector.user user_id + yield(user) + Devops::Db.connector.user_update user + end + end + end + end +end + diff --git a/devops-service/helpers/request.rb b/devops-service/app/api2/helpers/request.rb similarity index 100% rename from devops-service/helpers/request.rb rename to devops-service/app/api2/helpers/request.rb diff --git a/devops-service/helpers/version_2.rb b/devops-service/app/api2/helpers/version_2.rb similarity index 94% rename from devops-service/helpers/version_2.rb rename to devops-service/app/api2/helpers/version_2.rb index edcc30a..c201032 100644 --- a/devops-service/helpers/version_2.rb +++ b/devops-service/app/api2/helpers/version_2.rb @@ -5,7 +5,7 @@ require "sinatra/json" require "providers/provider_factory" module Devops - module Version2_0 + module API2_0 module Helpers def create_response msg, obj=nil, rstatus=200 @@ -23,11 +23,10 @@ module Devops def check_privileges cmd, p # somewhy REMOTE_USER is missing - user = request.env['HTTP_REMOTE_USER'] || request.env['REMOTE_USER'] - settings.mongo.check_user_privileges(user, cmd, p) + user = request.env['USER'] + user.check_privileges(cmd, p) end - def check_provider provider list = ::Provider::ProviderFactory.providers halt_response("Invalid provider '#{provider}', available providers: '#{list.join("', '")}'", 404) unless list.include?(provider) diff --git a/devops-service/routes/v2.0/bootstrap_templates.rb b/devops-service/app/api2/routes/bootstrap_templates.rb similarity index 79% rename from devops-service/routes/v2.0/bootstrap_templates.rb rename to devops-service/app/api2/routes/bootstrap_templates.rb index 1e1fc1c..93395e4 100644 --- a/devops-service/routes/v2.0/bootstrap_templates.rb +++ b/devops-service/app/api2/routes/bootstrap_templates.rb @@ -1,5 +1,5 @@ module Devops - module Version2_0 + module API2_0 module Routes module BootstrapTemplatesRoutes @@ -18,7 +18,10 @@ module Devops # [ # "omnibus" # ] - app.get_with_headers "/templates", :headers => [:accept], &Devops::Version2_0::Handler::BootstrapTemplates.get_bootstrap_templates + app.get_with_headers "/templates", :headers => [:accept] do + check_privileges("templates", "r") + json Devops::API2_0::Handler::BootstrapTemplates.new.get_templates + end puts "Bootstrap templates routes initialized" end diff --git a/devops-service/app/api2/routes/deploy.rb b/devops-service/app/api2/routes/deploy.rb new file mode 100644 index 0000000..045ec9f --- /dev/null +++ b/devops-service/app/api2/routes/deploy.rb @@ -0,0 +1,60 @@ + +module Devops + module API2_0 + module Routes + module DeployRoutes + + def self.registered(app) + + # Run chef-client on reserved server + # + # * *Request* + # - method : POST + # - headers : + # - Content-Type: application/json + # - body : + # { + # "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 + # } + # + # * *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") + stream() do |out| + status = [] + begin + Devops::API2_0::Handler::Deploy.new(request, params).deploy_stream(out, names, tags) + rescue IOError => e + logger.error e.message + break + end + end # stream + else + ids = Devops::API2_0::Handler::Deploy.new(request, params).deploy(names, tags) + 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 + json files + end + + end + + puts "Deploy routes initialized" + end + + end + end + end +end diff --git a/devops-service/routes/v2.0/filter.rb b/devops-service/app/api2/routes/filter.rb similarity index 62% rename from devops-service/routes/v2.0/filter.rb rename to devops-service/app/api2/routes/filter.rb index 6563e5e..b443ea8 100644 --- a/devops-service/routes/v2.0/filter.rb +++ b/devops-service/app/api2/routes/filter.rb @@ -1,20 +1,10 @@ module Devops - module Version2_0 + module API2_0 module Routes module FilterRoutes def self.registered(app) - app.before "/filter/:provider/image" do - protect! - check_headers :accept, :content_type - check_privileges("filter", "w") - check_provider(params[: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") - end - # Get list of images filters for :provider # # Devops can works with images from filters list only @@ -33,7 +23,11 @@ module Devops # [ # "36dc7618-4178-4e29-be43-286fbfe90f50" # ] - app.get_with_headers "/filter/:provider/images", :headers => [:accept], &Devops::Version2_0::Handler::Filter.get_filters + 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) + end hash = {} @@ -50,7 +44,14 @@ module Devops # ] -> array of image ids to add to filter # # * *Returns* : list of images filters for :provider - hash["PUT"] = Devops::Version2_0::Handler::Filter.add_filter + 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)}) + } # Delete image ids from filter for :provider # @@ -65,7 +66,14 @@ module Devops # ] -> array of image ids to delete from filter # # * *Returns* : list of images filters for :provider - hash["DELETE"] = Devops::Version2_0::Handler::Filter.delete_filter + 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)}) + } app.multi_routes "/filter/:provider/image", {:headers => [:accept, :content_type]}, hash diff --git a/devops-service/routes/v2.0/flavor.rb b/devops-service/app/api2/routes/flavor.rb similarity index 79% rename from devops-service/routes/v2.0/flavor.rb rename to devops-service/app/api2/routes/flavor.rb index d3abcf7..68a0559 100644 --- a/devops-service/routes/v2.0/flavor.rb +++ b/devops-service/app/api2/routes/flavor.rb @@ -1,5 +1,5 @@ module Devops - module Version2_0 + module API2_0 module Routes module FlavorRoutes @@ -31,7 +31,12 @@ module Devops # "disk": 20 # } # ] - app.get_with_headers "/flavors/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Flavor.get_flavors + app.get_with_headers "/flavors/:provider", :headers => [:accept] do + check_privileges("flavor", "r") + provider = params[:provider] + check_provider(provider) + json Devops::API2_0::Handler::Flavor.new(request, params).flavors + end puts "Flavor routes initialized" end diff --git a/devops-service/routes/v2.0/group.rb b/devops-service/app/api2/routes/group.rb similarity index 82% rename from devops-service/routes/v2.0/group.rb rename to devops-service/app/api2/routes/group.rb index ef450f4..ed8238e 100644 --- a/devops-service/routes/v2.0/group.rb +++ b/devops-service/app/api2/routes/group.rb @@ -1,6 +1,6 @@ # encoding: UTF-8 module Devops - module Version2_0 + module API2_0 module Routes module GroupRoutes @@ -44,7 +44,12 @@ module Devops # } # } # TODO: vpc support for ec2 - app.get_with_headers "/groups/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Group.get_groups + app.get_with_headers "/groups/:provider", :headers => [:accept] do#, &Devops::Version2_0::Handler::Group.get_groups + check_privileges("group", "r") + provider = params[:provider] + check_provider(provider) + json Devops::API2_0::Handler::Group.new(provider).groups(params) + end puts "Group routes initialized" end diff --git a/devops-service/routes/v2.0/image.rb b/devops-service/app/api2/routes/image.rb similarity index 70% rename from devops-service/routes/v2.0/image.rb rename to devops-service/app/api2/routes/image.rb index 8b71857..3eb228c 100644 --- a/devops-service/routes/v2.0/image.rb +++ b/devops-service/app/api2/routes/image.rb @@ -1,12 +1,9 @@ module Devops - module Version2_0 + module API2_0 module Routes module ImageRoutes def self.registered(app) - app.after %r{\A/image(/[\w]+)?\z} do - statistic - end # Get devops images list # @@ -27,7 +24,12 @@ module Devops # "id": "36dc7618-4178-4e29-be43-286fbfe90f50" # } # ] - app.get_with_headers "/images", :headers => [:accept], &Devops::Version2_0::Handler::Image.get_images + 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) + end # Get raw images for :provider # @@ -53,7 +55,11 @@ module Devops # "status": "ACTIVE" # } # ] - app.get_with_headers "/images/provider/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Image.get_provider_images + 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) + end # Create devops image # @@ -73,7 +79,12 @@ module Devops # # * *Returns* : # 201 - Created - app.post_with_headers "/image", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Image.create_image + 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) + create_response "Created", nil, 201 + end hash = {} # Get devops image by id @@ -91,7 +102,10 @@ module Devops # "bootstrap_template": null, # "id": "36dc7618-4178-4e29-be43-286fbfe90f50" # } - hash["GET"] = Devops::Version2_0::Handler::Image.get_image + hash["GET"] = lambda { |image_id| + check_privileges("image", "r") + json Devops::API2_0::Handler::Image.new(request, params).image(image_id) + } # Update devops image # @@ -111,7 +125,12 @@ module Devops # # * *Returns* : # 200 - Updated - hash["PUT"] = Devops::Version2_0::Handler::Image.update_image + 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) + create_response("Image '#{image_id}' has been updated") + } # Delete devops image # @@ -122,7 +141,11 @@ module Devops # # * *Returns* : # 200 - Deleted - hash["DELETE"] = Devops::Version2_0::Handler::Image.delete_image + hash["DELETE"] = lambda {|image_id| + check_privileges("image", "w") + Devops::API2_0::Handler::Image.delete_image + create_response("Image '#{image_id}' has been removed") + } app.multi_routes "/image/:image_id", {}, hash diff --git a/devops-service/app/api2/routes/key.rb b/devops-service/app/api2/routes/key.rb new file mode 100644 index 0000000..0b93348 --- /dev/null +++ b/devops-service/app/api2/routes/key.rb @@ -0,0 +1,93 @@ +require "json" + +module Devops + module API2_0 + module Routes + module KeyRoutes + + def self.registered(app) + # Get list of available ssh keys + # + # * *Request* + # - method : GET + # - headers : + # - Accept: application/json + # + # * *Returns* : array of strings + # [ + # { + # "scope": "system", -> 'system' - key was added by server, 'user' - key was added by user + # "id": "devops" + # } + # ] + app.get_with_headers "/keys", :headers => [:accept] do + check_privileges("key", "r") + json Devops::API2_0::Handler::Key.new.keys + end + + # Create ssh key on devops server + # + # * *Request* + # - method : POST + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "file_name": "key file name", + # "key_name": "key name", + # "content": "key content" + # } + # + # * *Returns* : + # 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) + create_response("Created", nil, 201) + end + + # Delete ssh key from devops server + # + # * *Request* + # - method : DELETE + # - headers : + # - Accept: application/json + # + # * *Returns* : + # 200 - Deleted + app.delete_with_headers "/key/:key", :headers => [:accept] do + check_privileges("key", "w") + servers = settings.mongo.servers_by_key params[:key] + 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 = settings.mongo.key params[:key] + begin + FileUtils.rm(k.path) + rescue + logger.error "Missing key file for #{params[:key]} - #{k.filename}" + end + r = settings.mongo.key_delete params[:key] + + r = Devops::API2_0::Handler::Key.new.delete params[:key] + return [500, r["err"].inspect] unless r["err"].nil? + create_response("Key '#{params[:key]}' removed") + + end + + puts "Key routes initialized" + end + + end + end + end +end diff --git a/devops-service/routes/v2.0/network.rb b/devops-service/app/api2/routes/network.rb similarity index 82% rename from devops-service/routes/v2.0/network.rb rename to devops-service/app/api2/routes/network.rb index c449de0..13c13f4 100644 --- a/devops-service/routes/v2.0/network.rb +++ b/devops-service/app/api2/routes/network.rb @@ -1,6 +1,6 @@ # encoding: UTF-8 module Devops - module Version2_0 + module API2_0 module Routes module NetworkRoutes @@ -32,7 +32,11 @@ module Devops # "id": "b14f8df9-ac27-48e2-8d65-f7ef78dc2654" # } # ] - app.get_with_headers "/networks/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Network.get_networks + 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 + end puts "Network routes initialized" end diff --git a/devops-service/routes/v2.0/project.rb b/devops-service/app/api2/routes/project.rb similarity index 69% rename from devops-service/routes/v2.0/project.rb rename to devops-service/app/api2/routes/project.rb index b314c21..79e626b 100644 --- a/devops-service/routes/v2.0/project.rb +++ b/devops-service/app/api2/routes/project.rb @@ -1,5 +1,5 @@ module Devops - module Version2_0 + module API2_0 module Routes module ProjectRoutes @@ -18,7 +18,10 @@ module Devops # [ # {"name" : "project_1"} # ] - app.get_with_headers "/projects", :headers => [:accept], &Devops::Version2_0::Handler::Project.get_projects + 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) + end # Get project by id # @@ -53,7 +56,10 @@ module Devops # "name": "project_1" # } hash = {} - hash["GET"] = Devops::Version2_0::Handler::Project.get_project + hash["GET"] = lambda {|project| + check_privileges("project", "r") + json Devops::API2_0::Handler::Project.new(request, params).project(project) + } # Update project and create chef roles # @@ -90,7 +96,13 @@ module Devops # # * *Returns* : # 200 - Updated - hash["PUT"] = Devops::Version2_0::Handler::Project.update_project + 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 + info = "Project '#{project.id}' has been updated." + r + create_response(info) + } # Delete project # @@ -106,7 +118,15 @@ module Devops # # * *Returns* : # 200 - Deleted - hash["DELETE"] = Devops::Version2_0::Handler::Project.delete_project + 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) + create_response(info) + } app.multi_routes "/project/:project", {}, hash # Get project servers @@ -135,7 +155,10 @@ module Devops # "id": "nstance id" # } # ] - app.get_with_headers "/project/:project/servers", :headers => [:accept], &Devops::Version2_0::Handler::Project.get_project_servers + 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) + end # Get project stacks # @@ -199,7 +222,15 @@ module Devops # # * *Returns* : # 201 - Created - app.post_with_headers "/project", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Project.create_project + 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) + res = Devops::API2_0::Handler::Project.new(request, params).create_project body + res = "Created. " + res + create_response(res, nil, 201) + end users_hash = {} # Add users to project environment @@ -219,7 +250,14 @@ module Devops # # * *Returns* : # 200 - Updated - users_hash["PUT"] = Devops::Version2_0::Handler::Project.update_project_users + 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) + create_response(info) + } # Delete users from project environment # @@ -238,7 +276,14 @@ module Devops # # * *Returns* : # 200 - Updated - users_hash["DELETE"] = Devops::Version2_0::Handler::Project.delete_project_users + 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) + create_response(info) + } app.multi_routes "/project/:id/user", {}, users_hash # Set run_list to project environment @@ -256,7 +301,13 @@ module Devops # # * *Returns* : # 200 - Updated - app.put_with_headers "/project/:id/:env/run_list", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Project.set_project_env_run_list + app.put_with_headers "/project/:id/:env/run_list", :headers => [:accept, :content_type] do |project, deploy_env| + 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) + create_response(info) + end # Run chef-client on reserved project servers # @@ -273,7 +324,25 @@ module Devops # } # # * *Returns* : text stream - app.post_with_headers "/project/:id/deploy", :headers => [:content_type], &Devops::Version2_0::Handler::Project.deploy_project + 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") + stream() do |out| + begin + status = handler.deploy_project_stream out, project, deploy_env, servers + out << create_status(status) + rescue IOError => e + logger.error e.message + end + end + else + json handler.deploy_project project, deploy_env, servers + end + end # Set project to archivated state # @@ -281,7 +350,11 @@ module Devops # - method : POST # - headers : # - Content-Type: application/json - app.post_with_headers "/project/:project/archive", :headers => [:content_type], &Devops::Version2_0::Handler::Project.archive_project + 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) + create_response(info) + end # Set project to normal state # @@ -289,7 +362,11 @@ module Devops # - method : POST # - headers : # - Content-Type: application/json - app.post_with_headers "/project/:project/unarchive", :headers => [:content_type], &Devops::Version2_0::Handler::Project.unarchive_project + 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) + create_response(info) + end # Test project environment # @@ -357,7 +434,10 @@ module Devops # }, # "message": "Test project 'project_1' and environment 'prod'" # } - app.post_with_headers "/project/test/:id/:env", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Project.test_project + 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) + end puts "Project routes initialized" end diff --git a/devops-service/routes/v2.0/provider.rb b/devops-service/app/api2/routes/provider.rb similarity index 72% rename from devops-service/routes/v2.0/provider.rb rename to devops-service/app/api2/routes/provider.rb index ab48ced..400a6d4 100644 --- a/devops-service/routes/v2.0/provider.rb +++ b/devops-service/app/api2/routes/provider.rb @@ -1,10 +1,9 @@ # encoding: UTF-8 -require "json" require "providers/provider_factory" module Devops - module Version2_0 + module API2_0 module Routes module ProviderRoutes @@ -22,7 +21,10 @@ module Devops # "ec2", # "openstack" # ] - app.get_with_headers "/providers", :headers => [:accept], &Devops::Version2_0::Handler::Provider.get_providers + 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 + end puts "Provider routes initialized" end diff --git a/devops-service/app/api2/routes/report.rb b/devops-service/app/api2/routes/report.rb new file mode 100644 index 0000000..48cdfa0 --- /dev/null +++ b/devops-service/app/api2/routes/report.rb @@ -0,0 +1,61 @@ +module Devops + module API2_0 + module Routes + module ReportRoutes + + 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} + 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} + end + + app.get_with_headers "/report/all/attributes/:name", headers: [:accept] do |name| + json Devops::API2_0::Handler::Report.new(request, params).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 + end + + app.get "/status/:id" do + r = Devops::API2_0::Handler::Report.new(request, params).status(params[:id]) + return [404, "Job with id '#{params[:id]}' not found"] if r.nil? + r + end + + puts "Report routes initialized" + end + + end + end + 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 new file mode 100644 index 0000000..b65e10f --- /dev/null +++ b/devops-service/app/api2/routes/script.rb @@ -0,0 +1,115 @@ + +module Devops + module Version2_0 + module Routes + module ScriptRoutes + + def self.registered(app) + # Get scripts names + # + # * *Request* + # - method : GET + # - headers : + # - Accept: application/json + # + # * *Returns* : + # [ + # "script_1" + # ] + app.get_with_headers "/scripts", :headers => [:accept] do + check_privileges("script", "r") + json Devops::API2_0::Handler::Script.new(request, params).scripts + end + + # Run command on node :node_name + # + # * *Request* + # - method : POST + # - body : + # command to run + # + # * *Returns* : text stream + app.post_with_statistic "/script/command/:node_name" do |node_name| + check_privileges("script", "x") + stream() do |out| + begin + Devops::API2_0::Handler::Script.new(request, params).execute_command(out, request.body.read, node_name) + rescue IOError => e + logger.error e.message + end + end + end + + # Run script :script_name on nodes + # + # * *Request* + # - method : POST + # - headers : + # - Content-Type: application/json + # - body : + # { + # "nodes": [], -> array of nodes names + # "params": [] -> array of script arguments + # } + # + # * *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 + rescue IOError => e + logger.error e.message + end + end + end + + hash = {} + # Create script :script_name + # + # * *Request* + # - method : PUT + # - headers : + # - Accept: application/json + # - body : script content + # + # * *Returns* : + # 201 - Created + hash["PUT"] = lambda { + 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) + } + + # Delete script :script_name + # + # * *Request* + # - method : Delete + # - headers : + # - Accept: application/json + # + # * *Returns* : + # 200 - Deleted + hash["DELETE"] = lambda { + 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") + } + app.multi_routes "/script/:script_name", {:headers => [:accept]}, hash + + puts "Script routes initialized" + end + + end + end + end +end diff --git a/devops-service/routes/v2.0/server.rb b/devops-service/app/api2/routes/server.rb similarity index 55% rename from devops-service/routes/v2.0/server.rb rename to devops-service/app/api2/routes/server.rb index bf1c13f..b82f35a 100644 --- a/devops-service/routes/v2.0/server.rb +++ b/devops-service/app/api2/routes/server.rb @@ -1,7 +1,7 @@ require "json" module Devops - module Version2_0 + module API2_0 module Routes module ServerRoutes @@ -23,7 +23,17 @@ module Devops # "chef_node_name": "chef name" # } # ] - app.get_with_headers "/servers", :headers => [:accept], &Devops::Version2_0::Handler::Server.get_servers + 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) + end # Get chef nodes list # @@ -38,7 +48,10 @@ module Devops # "chef_node_name": "chef name" # } # ] - app.get_with_headers "/servers/chef", :headers => [:accept], &Devops::Version2_0::Handler::Server.get_chef_servers + app.get_with_headers "/servers/chef", :headers => [:accept] do + check_privileges("server", "r") + json Devops::API2_0::Handler::Server.new(request, params).chef_servers + end # Get provider servers list # @@ -76,7 +89,10 @@ module Devops # "private_ip": "172.17.0.1" # } # ] - app.get_with_headers "/servers/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Server.get_provider_servers + app.get_with_headers "/servers/:provider", :headers => [:accept] do + check_privileges("server", "r") + json Devops::API2_0::Handler::Server.new(request, params).provider_servers(params[:provider]) + end # Get server info by :name # @@ -94,7 +110,10 @@ module Devops # } # ] hash = {} - hash["GET"] = Devops::Version2_0::Handler::Server.get_server + hash["GET"] = lambda {|id| + check_privileges("server", "r") + json Devops::API2_0::Handler::Server.new(request, params).server(id).to_hash + } # Delete devops server # @@ -110,7 +129,13 @@ module Devops # # * *Returns* : # 200 - Deleted - hash["DELETE"] = Devops::Version2_0::Handler::Server.delete_server + 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) + create_response(info, r) + } app.multi_routes "/server/:id", {:headers => [:accept, :content_type]}, hash # Create devops server @@ -133,7 +158,32 @@ module Devops # } # # * *Returns* : text stream - app.post_with_headers "/server", :headers => [:content_type], &Devops::Version2_0::Handler::Server.create_server + app.post_with_headers "/server", :headers => [: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") + stream() do |out| + begin + status = handler.create_server_stream out, body + out << create_status(status) + rescue IOError => e + logger.error e.message + end + end + else + json handler.create_server body + end + end # Pause devops server by name # @@ -149,7 +199,13 @@ module Devops # # * *Returns* : # 200 - Paused - app.post_with_headers "/server/:node_name/pause", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.pause_server + 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) + create_response(info) + end # Unpause devops server by name # @@ -165,7 +221,13 @@ module Devops # # * *Returns* : # 200 - Unpaused - app.post_with_headers "/server/:node_name/unpause", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.unpause_server + 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) + create_response(info) + end # Reserve devops server # @@ -181,7 +243,13 @@ module Devops # # * *Returns* : # 200 - Reserved - app.post_with_headers "/server/:node_name/reserve", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.reserve_server + 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) + create_response("Server '#{node_name}' has been reserved") + end # Unreserve devops server # @@ -197,7 +265,13 @@ module Devops # # * *Returns* : # 200 - Unreserved - app.post_with_headers "/server/:node_name/unreserve", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.unreserve_server + 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) + create_response("Server '#{node_name}' has been unreserved") + end # Bootstrap devops server # @@ -215,7 +289,32 @@ module Devops # } # # * *Returns* : text stream - app.post_with_headers "/server/bootstrap", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.bootstrap_server + 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") + stream() do |out| + begin + status = handler.bootstrap_server_stream out, body + out << create_status(status) + rescue IOError => e + logger.error e.message + end + end + else + handler.bootstrap_server(body) + end + end # Add external server to devops # @@ -236,7 +335,19 @@ module Devops # # * *Returns* : # 200 - Added - app.post_with_headers "/server/add", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.add_server + 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) + create_response(info) + end puts "Server routes initialized" end diff --git a/devops-service/app/api2/routes/stack.rb b/devops-service/app/api2/routes/stack.rb new file mode 100644 index 0000000..0b0144c --- /dev/null +++ b/devops-service/app/api2/routes/stack.rb @@ -0,0 +1,61 @@ +module Devops + module API2_0 + module Routes + module StackRoutes + + def self.registered(app) + + 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) + 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) + 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 + create_response "Created", m.to_hash, 201 + end + + hash = {} + + hash['GET'] = lambda { |stack_id| + check_privileges("stack", "r") + json Devops::API2_0::Handler::Stack.new(request, params).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) + 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 + 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) + 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) + end + + puts "Stack routes initialized" + end + + end + end + end +end diff --git a/devops-service/routes/v2.0/stack_presets.rb b/devops-service/app/api2/routes/stack_presets.rb similarity index 62% rename from devops-service/routes/v2.0/stack_presets.rb rename to devops-service/app/api2/routes/stack_presets.rb index 1e34816..85c2889 100644 --- a/devops-service/routes/v2.0/stack_presets.rb +++ b/devops-service/app/api2/routes/stack_presets.rb @@ -1,10 +1,10 @@ module Devops - module Version2_0 + module API2_0 module Routes module StackPresetRoutes def self.registered(app) - # Get list of available stack_presets + # Get list of available stack_template_presets # # * *Request* # - method : GET @@ -14,10 +14,12 @@ module Devops # * *Returns* : array of hashes # [ {id: 'preset id', template_preset_body: 'long body'} ] # - app.get_with_headers "/stack_presets", :headers => [:accept], &Devops::Version2_0::Handler::StackPreset.get_presets + 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) + end - - # Get information about stack_preset + # Get information about stack_template_preset # # * *Request* # - method : GET @@ -27,8 +29,10 @@ module Devops # * *Returns* : hash # {id: 'preset id', template_preset_body: 'long body'} # - app.get_with_headers "/stack_presets/:id", :headers => [:accept], &Devops::Version2_0::Handler::StackPreset.get_preset - + 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 + end # Build stack template from preset # @@ -38,7 +42,7 @@ module Devops # - Accept: application/json # - params : # - provider: string - # - stack_id: id of stack template to create + # - stack_template_id: id of stack template to create # - template_attrs: hash with template attributes # # TODO: not stack template, but stack itself @@ -50,7 +54,13 @@ module Devops # template_body: 'long body' # } # - app.post_with_headers "/stack_presets/:id/apply", :headers => [:accept], &Devops::Version2_0::Handler::StackPreset.apply + 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) + create_response 'Created', stack.to_hash, 201 + end puts "Stack template presets routes initialized" end diff --git a/devops-service/app/api2/routes/stack_template.rb b/devops-service/app/api2/routes/stack_template.rb new file mode 100644 index 0000000..322132d --- /dev/null +++ b/devops-service/app/api2/routes/stack_template.rb @@ -0,0 +1,46 @@ +module Devops + module API2_0 + module Routes + module StackTemplateRoutes + + 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) + 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) + 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) + create_response 'Created', model.to_hash, 201 + end + + hash = {} + + 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 + } + + 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) + create_response("Template '#{stack_template_id}' has been removed") + } + + app.multi_routes '/stack_template/:stack_template_id', {}, hash + + puts "Stack_template routes initialized" + end + + end + end + end +end diff --git a/devops-service/routes/v2.0/tag.rb b/devops-service/app/api2/routes/tag.rb similarity index 56% rename from devops-service/routes/v2.0/tag.rb rename to devops-service/app/api2/routes/tag.rb index d7577b9..30fe0e4 100644 --- a/devops-service/routes/v2.0/tag.rb +++ b/devops-service/app/api2/routes/tag.rb @@ -1,5 +1,5 @@ module Devops - module Version2_0 + module API2_0 module Routes module TagRoutes @@ -17,7 +17,10 @@ module Devops # [ # "tag_1" # ] - hash["GET"] = Devops::Version2_0::Handler::Tag.get_tags + hash["GET"] = lambda { + check_privileges("server", "r") + json Devops::API2_0::Handler::Tag.new(params[:node_name]).tags() + } # Set tags list to :node_name # @@ -33,7 +36,13 @@ module Devops # # * *Returns* : # 200 - hash["POST"] = Devops::Version2_0::Handler::Tag.set_tags + hash["POST"] = lambda { + 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) + } # Delete tags from :node_name # @@ -49,7 +58,13 @@ module Devops # # * *Returns* : # 200 - hash["DELETE"] = Devops::Version2_0::Handler::Tag.unset_tags + hash["DELETE"] = lambda { + 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) + } app.multi_routes "/tags/:node_name", {:headers => [:accept, :content_type]}, hash puts "Tag routes initialized" diff --git a/devops-service/routes/v2.0/user.rb b/devops-service/app/api2/routes/user.rb similarity index 50% rename from devops-service/routes/v2.0/user.rb rename to devops-service/app/api2/routes/user.rb index 5a3ea28..912d566 100644 --- a/devops-service/routes/v2.0/user.rb +++ b/devops-service/app/api2/routes/user.rb @@ -1,5 +1,5 @@ module Devops - module Version2_0 + module API2_0 module Routes module UserRoutes @@ -33,7 +33,11 @@ module Devops # "id": "test" # } # ] - app.get_with_headers "/users", :headers => [:accept], &Devops::Version2_0::Handler::User.get_users + app.get_with_headers "/users", :headers => [:accept] do#, &Devops::API2_0::Handler::User.get_users + check_privileges("user", "r") + users = Devops::API2_0::Handler::User.new.users.map {|i| h = i.to_hash; h.delete("password"); h} + json users + end # Create user # @@ -51,7 +55,15 @@ module Devops # # * *Returns* : # 201 - Created - app.post_with_headers "/user", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::User.create_user + app.post_with_headers "/user", :headers => [:accept, :content_type] do#, &Devops::API2_0::Handler::User.create_user + 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) + create_response("Created", nil, 201) + end hash = {} # Delete user @@ -63,7 +75,24 @@ module Devops # # * *Returns* : # 200 - Deleted - hash["DELETE"] = Devops::Version2_0::Handler::User.delete_user + hash["DELETE"] = lambda { + 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") + } # Change user privileges # @@ -80,7 +109,14 @@ module Devops # # * *Returns* : # 200 - Updated - hash["PUT"] = Devops::Version2_0::Handler::User.change_user_privileges + hash["PUT"] = lambda { + 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) + create_response("Updated") + } app.multi_routes "/user/:user", {:headers => [:accept, :content_type]}, hash # Change user email/password @@ -97,7 +133,20 @@ module Devops # # * *Returns* : # 200 - Updated - app.put_with_headers %r{\A/user/#{DevopsConfig::OBJECT_NAME}/(email|password)\z}, :headers => [:accept, :content_type], &Devops::Version2_0::Handler::User.change_user_email_or_password + 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 + + 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}=", p) + create_response("Updated") + end puts "User routes initialized" end diff --git a/devops-service/routes/v2.0.rb b/devops-service/app/api2/routes/v2.0.rb similarity index 73% rename from devops-service/routes/v2.0.rb rename to devops-service/app/api2/routes/v2.0.rb index dbe1346..f39a344 100644 --- a/devops-service/routes/v2.0.rb +++ b/devops-service/app/api2/routes/v2.0.rb @@ -1,20 +1,32 @@ require "sinatra/base" require "sinatra/streaming" -require "helpers/version_2" require "json" +require "fog" + require "auth/devops_auth" +require "exceptions/invalid_record" +require "exceptions/record_not_found" +require "exceptions/dependency_error" +require "exceptions/conflict_exception" +require 'core/devops-logger' + +require_relative "../helpers/version_2" module Devops class Api2 < Sinatra::Base include Sinatra::JSON helpers Sinatra::Streaming - helpers Devops::Version2_0::Helpers + helpers Devops::API2_0::Helpers register Sinatra::DevopsAuth configure :production do + config = DevopsConfig.config + log_file = File.join(config[:log_dir], "devops-api2.log") + logger = DevopsLogger.create(log_file, Logger::DEBUG) + use Rack::CommonLogger, logger disable :dump_errors disable :show_exceptions set :logging, Logger::INFO @@ -22,11 +34,14 @@ module Devops end configure :development do - set :logging, Logger::DEBUG + config = DevopsConfig.config + log_file = File.join(config[:log_dir], "devops-api2.log") + logger = DevopsLogger.create(log_file, Logger::DEBUG) + use Rack::CommonLogger, logger disable :raise_errors # disable :dump_errors set :show_exceptions, :after_handler - puts "Development mode" + logger.info "Development mode" end not_found do @@ -58,6 +73,12 @@ module Devops halt_response(e.message, 400) end + error ConflictException do + e = env["sinatra.error"] + logger.warn e.message + halt_response(e.message, 409) + end + error InvalidPrivileges do e = env["sinatra.error"] logger.warn e.message diff --git a/devops-service/app/client/devops-client.rb b/devops-service/app/client/devops-client.rb index 80a3448..36ac9f0 100644 --- a/devops-service/app/client/devops-client.rb +++ b/devops-service/app/client/devops-client.rb @@ -1,4 +1,6 @@ require 'sinatra/base' +require 'core/devops-logger' +require 'core/devops-config' class Client < Sinatra::Base @@ -7,6 +9,29 @@ class Client < Sinatra::Base @@config = DevopsConfig.config end + configure :production do + config = DevopsConfig.config + log_file = File.join(config[:log_dir], "devops-client.log") + logger = DevopsLogger.create(log_file, Logger::INFO) + use Rack::CommonLogger, logger + disable :dump_errors + disable :show_exceptions +# set :logging, Logger::INFO + logger.info "Production mode" + end + + configure :development do + config = DevopsConfig.config + log_file = File.join(config[:log_dir], "devops-client.log") + logger = DevopsLogger.create(log_file, Logger::DEBUG) + use Rack::CommonLogger, logger +# set :logging, Logger::DEBUG + disable :raise_errors +# disable :dump_errors + set :show_exceptions, :after_handler + logger.info "Development mode" + end + # Route to download devops client get "/devops-client.gem" do begin diff --git a/devops-service/app/devops-api2.rb b/devops-service/app/devops-api2.rb index 88eb263..adeb676 100644 --- a/devops-service/app/devops-api2.rb +++ b/devops-service/app/devops-api2.rb @@ -1,81 +1,78 @@ module Devops - module Application - class DevopsApi2Application < Application + class DevopsApi2Application < Application - def prepare - require "routes/v2.0" - require "routes/v2.0/handlers/provider" - require "routes/v2.0/handlers/bootstrap_templates" - require "routes/v2.0/handlers/deploy" - require "routes/v2.0/handlers/filter" - require "routes/v2.0/handlers/flavor" - require "routes/v2.0/handlers/group" - require "routes/v2.0/handlers/image" - require "routes/v2.0/handlers/network" - require "routes/v2.0/handlers/key" - require "routes/v2.0/handlers/project" - require "routes/v2.0/handlers/script" - require "routes/v2.0/handlers/status" - require "routes/v2.0/handlers/tag" - require "routes/v2.0/handlers/user" - require "routes/v2.0/handlers/server" - require "routes/v2.0/handlers/stack" - require "routes/v2.0/handlers/stack_template" - require "routes/v2.0/stack_presets" - require "routes/v2.0/handlers/report" + def prepare + require_relative "api2/routes/v2.0" + require_relative "api2/handlers/provider" + require_relative "api2/handlers/flavor" + require_relative "api2/handlers/filter" + require_relative "api2/handlers/group" + require_relative "api2/handlers/user" + require_relative "api2/handlers/network" + require_relative "api2/handlers/report" + require_relative "api2/handlers/deploy" + require_relative "api2/handlers/script" + require_relative "api2/handlers/bootstrap_templates" + require_relative "api2/handlers/key" + require_relative "api2/handlers/tag" + require_relative "api2/handlers/server" + require_relative "api2/handlers/image" + require_relative "api2/handlers/project" - require 'lib/stubber' - end - - def init - config = DevopsConfig.config - Devops::Api2.set :devops_home, config[:devops_dir] - #set :config, config - - Devops::Api2.set :keys_dir, (config[:keys_dir] || File.join(config[:devops_dir], "files/keys")) - 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 - ::Provider::ProviderFactory.init(config) - - Stubber.stub_providers!(config[:stub_providers]) - end - - def routes - require "routes/v2.0/flavor" - require "routes/v2.0/image" - require "routes/v2.0/filter" - require "routes/v2.0/network" - require "routes/v2.0/group" - require "routes/v2.0/deploy" - require "routes/v2.0/project" - require "routes/v2.0/key" - require "routes/v2.0/user" - require "routes/v2.0/provider" - require "routes/v2.0/tag" - require "routes/v2.0/server" - require "routes/v2.0/script" - require "routes/v2.0/status" - require "routes/v2.0/bootstrap_templates" - require "routes/v2.0/stack" - require "routes/v2.0/stack_template" - require "routes/v2.0/handlers/stack_preset" - require "routes/v2.0/report" - - routes = Devops::Version2_0::Routes.constants.collect{|s| Devops::Version2_0::Routes.const_get(s)}.select {|const| const.class == Module} - routes.each do |r| - Devops::Api2.register r - end - Routes.route "/v2.0", Devops::Api2 - end - - private - def init_mongo - Devops::Api2.set :mongo, Devops::Db.connector - end + require_relative "api2/handlers/stack" + require_relative "api2/handlers/stack_template" + require_relative "api2/handlers/stack_preset" + require 'lib/stubber' end + + def init + config = DevopsConfig.config + Devops::Api2.set :devops_home, config[:devops_dir] + #set :config, config + + Devops::Api2.set :keys_dir, (config[:keys_dir] || File.join(config[:devops_dir], "files/keys")) + 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 + ::Provider::ProviderFactory.init(config) + + Stubber.stub_providers! if config[:stub_classes] + end + + def routes + require_relative "api2/routes/flavor" + require_relative "api2/routes/image" + require_relative "api2/routes/filter" + require_relative "api2/routes/network" + require_relative "api2/routes/group" + require_relative "api2/routes/deploy" + require_relative "api2/routes/project" + require_relative "api2/routes/key" + require_relative "api2/routes/user" + require_relative "api2/routes/provider" + require_relative "api2/routes/tag" + require_relative "api2/routes/server" + require_relative "api2/routes/script" + require_relative "api2/routes/bootstrap_templates" + require_relative "api2/routes/stack" + require_relative "api2/routes/stack_template" + require_relative "api2/routes/stack_presets" + require_relative "api2/routes/report" + + routes = Devops::API2_0::Routes.constants.collect{|s| Devops::API2_0::Routes.const_get(s)}.select {|const| const.class == Module} + routes.each do |r| + Devops::Api2.register r + end + Routes.route "/v2.0", Devops::Api2 + end + + private + def init_mongo + Devops::Api2.set :mongo, Devops::Db.connector + end + end end diff --git a/devops-service/app/devops-application.rb b/devops-service/app/devops-application.rb deleted file mode 100644 index d11676f..0000000 --- a/devops-service/app/devops-application.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Devops - module Application - class Application - - @@applications = [] - - def self.inherited(base) - @@applications << base.new - puts "Devops application '#{base}' has been added" - end - - def self.applications - @@applications - end - - def prepare - - end - - def init - - end - - def routes - end - end - end -end diff --git a/devops-service/app/devops-client.rb b/devops-service/app/devops-client.rb index a303a53..79813f0 100644 --- a/devops-service/app/devops-client.rb +++ b/devops-service/app/devops-client.rb @@ -1,14 +1,12 @@ module Devops - module Application - class DevopsClientApplication < Application + class DevopsClientApplication < Application - def prepare - require "app/client/devops-client" - end + def prepare + require "app/client/devops-client" + end - def routes - Routes.route "/client", ::Client.new - end + def routes + Routes.route "/client", ::Client.new end end end diff --git a/devops-service/app/devops-version.rb b/devops-service/app/devops-version.rb index 0cfadc0..3bc8445 100644 --- a/devops-service/app/devops-version.rb +++ b/devops-service/app/devops-version.rb @@ -1,14 +1,12 @@ module Devops - module Application - class DevopsVersionApplication < Application + class DevopsVersionApplication < Application - def prepare - require "app/version/version" - end + def prepare + require "app/version/version" + end - def routes - Routes.route "/version", ::DevopsVersion.new - end + def routes + Routes.route "/version", ::DevopsVersion.new end end end diff --git a/devops-service/app/sidekiq_web.rb b/devops-service/app/sidekiq_web.rb index c53d3db..4a3970f 100644 --- a/devops-service/app/sidekiq_web.rb +++ b/devops-service/app/sidekiq_web.rb @@ -1,17 +1,22 @@ +require 'core/devops-logger' +require 'core/devops-config' + module Devops - module Application - class SidekiqApplication < Application - - def prepare - require "sidekiq/web" - end - - def routes - Devops::Routes.route "/sidekiq", Sidekiq::Web - end + class SidekiqApplication < Application + def prepare + require "sidekiq/web" end + + def routes + config = DevopsConfig.config + log_file = File.join(config[:log_dir], "devops-sidekiq-web.log") + logger = DevopsLogger.create(log_file, Logger::INFO) + Sidekiq::Web.use Rack::CommonLogger, logger + Devops::Routes.route "/sidekiq", Sidekiq::Web + end + end end diff --git a/devops-service/app/version/version.rb b/devops-service/app/version/version.rb index 277675f..b32fcd3 100644 --- a/devops-service/app/version/version.rb +++ b/devops-service/app/version/version.rb @@ -2,7 +2,7 @@ require 'sinatra/base' class DevopsVersion < Sinatra::Base - VERSION = "3.0.0" + VERSION = "2.3.0" get "/" do VERSION diff --git a/devops-service/applications.rb b/devops-service/applications.rb new file mode 100644 index 0000000..cbccf94 --- /dev/null +++ b/devops-service/applications.rb @@ -0,0 +1,4 @@ +require "app/sidekiq_web" +require "app/devops-client" +require "app/devops-version" +require "app/devops-api2" diff --git a/devops-service/auth/devops_auth.rb b/devops-service/auth/devops_auth.rb index b7f2976..2e84e53 100644 --- a/devops-service/auth/devops_auth.rb +++ b/devops-service/auth/devops_auth.rb @@ -12,8 +12,9 @@ module Sinatra if @auth.provided? and @auth.basic? and @auth.credentials c = @auth.credentials begin - Devops::Db.connector.user_auth(c[0], c[1]) + u = Devops::Db.connector.user_auth(c[0], c[1]) request.env['REMOTE_USER'] = c[0] + request.env['USER'] = u true rescue RecordNotFound => e false diff --git a/devops-service/commands/bootstrap_templates.rb b/devops-service/commands/bootstrap_templates.rb index fd53941..3d81e12 100644 --- a/devops-service/commands/bootstrap_templates.rb +++ b/devops-service/commands/bootstrap_templates.rb @@ -2,7 +2,8 @@ module BootstrapTemplatesCommands def get_templates res = [] - Dir.foreach("#{ENV["HOME"]}/.chef/bootstrap/") {|f| res.push(f[0..-5]) if f.end_with?(".erb")} if File.exists? "#{ENV["HOME"]}/.chef/bootstrap/" + dir = "#{ENV["HOME"]}/.chef/bootstrap/" + Dir.foreach(dir) {|f| res.push(f[0..-5]) if f.end_with?(".erb")} if File.exists? dir res end diff --git a/devops-service/commands/deploy_env.rb b/devops-service/commands/deploy_env.rb index 3f1d9ad..411db1c 100644 --- a/devops-service/commands/deploy_env.rb +++ b/devops-service/commands/deploy_env.rb @@ -1,4 +1,3 @@ -require "db/exceptions/invalid_record" require "commands/image" module DeployEnvCommands diff --git a/devops-service/commands/server.rb b/devops-service/commands/server.rb index 3f6b2f6..d690f93 100644 --- a/devops-service/commands/server.rb +++ b/devops-service/commands/server.rb @@ -1,6 +1,6 @@ require "commands/knife_commands" require "commands/deploy" -require "db/exceptions/record_not_found" +require "exceptions/record_not_found" module ServerCommands diff --git a/devops-service/config.rb b/devops-service/config.rb index 5b07675..982b00b 100644 --- a/devops-service/config.rb +++ b/devops-service/config.rb @@ -1,5 +1,5 @@ -# path to log file -config[:log_file] = "/path/to/log" +# path to log dir +config[:log_dir] = "/path/to/log" # path to chef knife.rb file config[:knife_config_file] = "/path/to/.chef/knife.rb" # role name separator @@ -43,4 +43,4 @@ config[:debug] = true # set it to :all or [:ec2] to stub calls to selected providers # or to false to disable stubbing -config[:stub_providers] = false \ No newline at end of file +config[:stub_providers] = false diff --git a/devops-service/config.ru b/devops-service/config.ru index 4354bf3..6626b13 100644 --- a/devops-service/config.ru +++ b/devops-service/config.ru @@ -4,11 +4,12 @@ require "rubygems" require "bundler/setup" require 'byebug' -require_relative "devops-service" -require_relative "devops_config" -require_relative "devops-routes" - root = File.dirname(__FILE__) +$:.push root + +require_relative "core/devops-service" +require_relative "core/devops-config" +require_relative "core/devops-routes" # Read configuration file DevopsConfig.read @@ -27,6 +28,10 @@ config[:report_dir_v2] = File.expand_path(File.join(config[:devops_dir], "report DevopsService.init puts Devops::Routes.routes +if Devops::Routes.routes.empty? + puts "No applications" + exit -1 +end Devops::Routes.routes.each do |p, c| map(p) do run c diff --git a/devops-service/core/devops-application.rb b/devops-service/core/devops-application.rb new file mode 100644 index 0000000..d959418 --- /dev/null +++ b/devops-service/core/devops-application.rb @@ -0,0 +1,26 @@ +module Devops + class Application + + @@applications = [] + + def self.inherited(base) + @@applications << base.new + puts "Devops application '#{base}' has been added" + end + + def self.applications + @@applications + end + + def prepare + + end + + def init + + end + + def routes + end + end +end diff --git a/devops-service/devops_config.rb b/devops-service/core/devops-config.rb similarity index 95% rename from devops-service/devops_config.rb rename to devops-service/core/devops-config.rb index 53b0c13..b40466d 100644 --- a/devops-service/devops_config.rb +++ b/devops-service/core/devops-config.rb @@ -9,7 +9,7 @@ class DevopsConfig class << self def read config_file=nil if config_file.nil? - config_file = ENV['DEVOPS_CONFIG'] || ENV['CONFIG'] || File.join(File.dirname(__FILE__), 'config.rb') + config_file = ENV['DEVOPS_CONFIG'] || ENV['CONFIG'] || File.join(File.dirname(__FILE__), '../config.rb') end config = {:url_prefix => ""} if File.exists? config_file diff --git a/devops-service/devops_db.rb b/devops-service/core/devops-db.rb similarity index 93% rename from devops-service/devops_db.rb rename to devops-service/core/devops-db.rb index bd01610..02407bc 100644 --- a/devops-service/devops_db.rb +++ b/devops-service/core/devops-db.rb @@ -1,3 +1,5 @@ +require "db/mongo/mongo_connector" + module Devops class Db diff --git a/devops-service/loader.rb b/devops-service/core/devops-loader.rb similarity index 100% rename from devops-service/loader.rb rename to devops-service/core/devops-loader.rb diff --git a/devops-service/core/devops-logger.rb b/devops-service/core/devops-logger.rb new file mode 100644 index 0000000..8963f2b --- /dev/null +++ b/devops-service/core/devops-logger.rb @@ -0,0 +1,16 @@ +require "logger" + +class DevopsLogger + + def self.create out, level=Logger::INFO, format='%a %d-%m-%Y %H%M ' + @_logger = Logger.new out + @_logger.level = level + @_logger.datetime_format = format + @_logger.debug("Logger has been created") + @_logger + end + + def self.logger + @_logger + end +end diff --git a/devops-service/devops-routes.rb b/devops-service/core/devops-routes.rb similarity index 100% rename from devops-service/devops-routes.rb rename to devops-service/core/devops-routes.rb diff --git a/devops-service/devops-service.rb b/devops-service/core/devops-service.rb similarity index 80% rename from devops-service/devops-service.rb rename to devops-service/core/devops-service.rb index 8490cc8..31984e6 100644 --- a/devops-service/devops-service.rb +++ b/devops-service/core/devops-service.rb @@ -1,29 +1,29 @@ require "wisper" -$:.push File.dirname(__FILE__) +=begin require "db/exceptions/invalid_record" require "db/exceptions/record_not_found" require "exceptions/dependency_error" require "db/validators/all" -require "db/mongo/mongo_connector" require "providers/provider_factory" require "fog" -require "loader" -require "devops_db" -require "devops_logger" - require_relative "routes/v2.0" require "hooks" -require "app/devops-application" -require "app/devops-client" -require "app/sidekiq_web" -require "app/devops-version" -require "app/devops-api2" +=end -require_relative "sinatra/methods_with_headers" +require_relative "devops-loader" +require_relative "devops-db" +require_relative "devops-logger" +require_relative "devops-application" + +require_relative "../sinatra/methods_with_headers" + +require_relative "../applications" +#root = File.expand_path("../", __FILE__) +#$:.push root class DevopsService @@ -51,7 +51,7 @@ class DevopsService DevopsLogger.create STDOUT routes - apps = Devops::Application::Application.applications + apps = Devops::Application.applications apps.each do |a| a.prepare end @@ -91,4 +91,4 @@ class DevopsService end -require "wisper_fix" +require_relative "../wisper_fix" diff --git a/devops-service/db/mongo/connectors/base.rb b/devops-service/db/mongo/connectors/base.rb index 36f9047..278519e 100644 --- a/devops-service/db/mongo/connectors/base.rb +++ b/devops-service/db/mongo/connectors/base.rb @@ -1,5 +1,5 @@ -require "db/exceptions/record_not_found" -require "db/exceptions/invalid_record" +require "exceptions/record_not_found" +require "exceptions/invalid_record" require "exceptions/invalid_command" require "exceptions/invalid_privileges" diff --git a/devops-service/db/mongo/connectors/user.rb b/devops-service/db/mongo/connectors/user.rb index d2d5650..bf83e69 100644 --- a/devops-service/db/mongo/connectors/user.rb +++ b/devops-service/db/mongo/connectors/user.rb @@ -1,3 +1,4 @@ +require "db/mongo/models/user" module Connectors class User < Base include Helpers::InsertCommand, @@ -13,6 +14,7 @@ module Connectors def user_auth user, password u = collection.find('_id' => user, 'password' => password).to_a.first raise RecordNotFound.new('Invalid username or password') if u.nil? + model_from_bson(u) end def users(ids=nil) @@ -33,19 +35,6 @@ module Connectors collection.insert(root.to_mongo_hash) end - def check_user_privileges(id, cmd, required_privelege) - user = show(id) - - unless %w(r w x).include?(required_privelege) - raise InvalidPrivileges.new("Access internal problem with privilege '#{required_privelege}'") - end - - unless user.can?(cmd, required_privelege) - raise InvalidPrivileges.new("Access denied for '#{user.id}'") - end - true - end - private def model_from_bson(bson) 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 fa6355d..7a88483 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,5 +1,4 @@ require "db/mongo/models/mongo_model" -require "db/exceptions/invalid_record" require "providers/provider_factory" require "commands/deploy_env" diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb index b0f9092..50b12cb 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb @@ -1,4 +1,11 @@ require "db/mongo/models/deploy_env/deploy_env_base" +require "db/validators/deploy_env/run_list" +require "db/validators/deploy_env/expiration" +require "db/validators/deploy_env/users" +require "db/validators/deploy_env/flavor" +require "db/validators/deploy_env/image" +require "db/validators/deploy_env/subnet_belongs_to_provider" +require "db/validators/deploy_env/groups" module Devops module Model diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_multi.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_multi.rb index 9fc5522..a0cca3e 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_multi.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_multi.rb @@ -1,5 +1,4 @@ require "db/mongo/models/mongo_model" -require "db/exceptions/invalid_record" require "commands/deploy_env" module Devops diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_openstack.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_openstack.rb index 99c5bb2..263f7c8 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_openstack.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_openstack.rb @@ -1,5 +1,13 @@ require "db/mongo/models/deploy_env/deploy_env_base" require "providers/provider_factory" +require "db/validators/deploy_env/run_list" +require "db/validators/deploy_env/expiration" +require "db/validators/deploy_env/users" +require "db/validators/deploy_env/flavor" +require "db/validators/deploy_env/image" +require "db/validators/deploy_env/subnet_not_empty" +require "db/validators/deploy_env/subnet_belongs_to_provider" +require "db/validators/deploy_env/groups" module Devops module Model diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_static.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_static.rb index bab2ad3..5df72e2 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_static.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_static.rb @@ -1,4 +1,7 @@ require "db/mongo/models/deploy_env/deploy_env_base" +require "db/validators/deploy_env/run_list" +require "db/validators/deploy_env/expiration" +require "db/validators/deploy_env/users" module Devops module Model diff --git a/devops-service/db/mongo/models/image.rb b/devops-service/db/mongo/models/image.rb index b20d545..f537248 100644 --- a/devops-service/db/mongo/models/image.rb +++ b/devops-service/db/mongo/models/image.rb @@ -1,5 +1,6 @@ -require "db/exceptions/invalid_record" require "db/mongo/models/mongo_model" +require "db/validators/image/bootstrap_template" +require "db/validators/image/image_in_filter" module Devops module Model diff --git a/devops-service/db/mongo/models/key.rb b/devops-service/db/mongo/models/key.rb index 9746ab2..74184e4 100644 --- a/devops-service/db/mongo/models/key.rb +++ b/devops-service/db/mongo/models/key.rb @@ -1,6 +1,7 @@ -require "db/exceptions/invalid_record" require "db/mongo/models/mongo_model" require "json" +require "db/validators/key/file_existence" +require "db/validators/key/scope" module Devops module Model diff --git a/devops-service/db/mongo/models/mongo_model.rb b/devops-service/db/mongo/models/mongo_model.rb index a32d764..a880934 100644 --- a/devops-service/db/mongo/models/mongo_model.rb +++ b/devops-service/db/mongo/models/mongo_model.rb @@ -1,5 +1,5 @@ require "providers/provider_factory" -require "db/exceptions/invalid_record" +require "exceptions/invalid_record" require "json" require 'db/validators/all' diff --git a/devops-service/db/mongo/models/project.rb b/devops-service/db/mongo/models/project.rb index 20cc25b..1fe6345 100644 --- a/devops-service/db/mongo/models/project.rb +++ b/devops-service/db/mongo/models/project.rb @@ -1,10 +1,9 @@ -require "db/exceptions/invalid_record" -require "db/exceptions/record_not_found" require "db/mongo/models/deploy_env/deploy_env_factory" require "db/mongo/models/user" 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" module Devops diff --git a/devops-service/db/mongo/models/report.rb b/devops-service/db/mongo/models/report.rb index dce0489..5485dd2 100644 --- a/devops-service/db/mongo/models/report.rb +++ b/devops-service/db/mongo/models/report.rb @@ -1,4 +1,3 @@ -require "db/exceptions/invalid_record" require "db/mongo/models/mongo_model" module Devops diff --git a/devops-service/db/mongo/models/server.rb b/devops-service/db/mongo/models/server.rb index ea93ea7..96a7c6e 100644 --- a/devops-service/db/mongo/models/server.rb +++ b/devops-service/db/mongo/models/server.rb @@ -1,4 +1,3 @@ -require "db/exceptions/invalid_record" require "db/mongo/models/mongo_model" module Devops diff --git a/devops-service/db/mongo/models/user.rb b/devops-service/db/mongo/models/user.rb index 73fdc05..9e50a3f 100644 --- a/devops-service/db/mongo/models/user.rb +++ b/devops-service/db/mongo/models/user.rb @@ -1,4 +1,4 @@ -require "db/exceptions/invalid_record" +require "exceptions/invalid_record" require "exceptions/invalid_command" require "db/mongo/models/mongo_model" @@ -70,15 +70,11 @@ module Devops o end - def can?(command, privilege) - p = self.privileges[command] || [] - p.include?(privilege) - end - - def check_privilege cmd, priv - p = self.privileges[cmd] - return false if p.nil? - return p.include?(priv) + def check_privileges cmd, required_privelege + unless PRIVILEGES.include?(required_privelege) + raise InvalidPrivileges.new("Access internal problem with privilege '#{required_privelege}'") + end + can?(cmd, required_privelege) end def self.create_root @@ -89,6 +85,12 @@ module Devops end private + + def can?(command, privilege) + p = self.privileges[command] || [] + p.include?(privilege) + end + def privileges_with_value value, options={} privileges = {} [ diff --git a/devops-service/db/validators/base.rb b/devops-service/db/validators/base.rb index a195053..ff3245e 100644 --- a/devops-service/db/validators/base.rb +++ b/devops-service/db/validators/base.rb @@ -1,40 +1,42 @@ -class Validators::Base +module Validators + class Base - def initialize(model, options={}) - @model = model - @options = options - end + def initialize(model, options={}) + @model = model + @options = options + end - def validate! - raise InvalidRecord.new(message) unless valid? - end + def validate! + raise InvalidRecord.new(message) unless valid? + end - def valid? - raise 'override me' - end + def valid? + raise 'override me' + end - def message - raise 'override me' - end + def message + raise 'override me' + end - class << self - private + class << self + private - # this method delegates @valid? and @message methods to helper validator, passed as block - def delegate_to_helper_validator(&block) + # this method delegates @valid? and @message methods to helper validator, passed as block + def delegate_to_helper_validator(&block) - define_method :helper_validator do - @helper_validator ||= self.instance_eval(&block) - end + define_method :helper_validator do + @helper_validator ||= self.instance_eval(&block) + end - define_method :valid? do - self.helper_validator.valid? - end + define_method :valid? do + self.helper_validator.valid? + end - define_method :message do - self.helper_validator.message + define_method :message do + self.helper_validator.message + end end end - end + end end diff --git a/devops-service/db/validators/deploy_env/run_list.rb b/devops-service/db/validators/deploy_env/run_list.rb index c37eec1..d124a27 100644 --- a/devops-service/db/validators/deploy_env/run_list.rb +++ b/devops-service/db/validators/deploy_env/run_list.rb @@ -2,8 +2,10 @@ # not only in deploy env model module Validators - class DeployEnv::RunList < Base + module DeployEnv + class RunList < Base - delegate_to_helper_validator { Helpers::RunList.new(@model.run_list) } + delegate_to_helper_validator { Helpers::RunList.new(@model.run_list) } + end end end diff --git a/devops-service/db/validators/image/bootstrap_template.rb b/devops-service/db/validators/image/bootstrap_template.rb index 5bf37e4..b1d53dc 100644 --- a/devops-service/db/validators/image/bootstrap_template.rb +++ b/devops-service/db/validators/image/bootstrap_template.rb @@ -1,20 +1,24 @@ require "commands/bootstrap_templates" +require "db/validators/base" + module Validators - class Image::BootstrapTemplate < Base + module Image + class BootstrapTemplate < Base - include BootstrapTemplatesCommands + include BootstrapTemplatesCommands - def valid? - if @model.bootstrap_template - templates = get_templates - templates.include?(@model.bootstrap_template) - else - true + def valid? + if @model.bootstrap_template + templates = get_templates + templates.include?(@model.bootstrap_template) + else + true + end end - end - def message - "Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'" + def message + "Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'" + end end end end diff --git a/devops-service/db/validators/key/file_existence.rb b/devops-service/db/validators/key/file_existence.rb index a14a6b5..e11885a 100644 --- a/devops-service/db/validators/key/file_existence.rb +++ b/devops-service/db/validators/key/file_existence.rb @@ -1,7 +1,9 @@ module Validators - class Key::FileExistence < Base + module Key + class FileExistence < Base - delegate_to_helper_validator { Helpers::FileExistence.new(@model.path) } + delegate_to_helper_validator { Helpers::FileExistence.new(@model.path) } + end end end diff --git a/devops-service/devops_logger.rb b/devops-service/devops_logger.rb deleted file mode 100644 index 10bf2b1..0000000 --- a/devops-service/devops_logger.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "logger" - -class DevopsLogger - - def self.create out, level=Logger::INFO - @_logger = Logger.new out - @_logger.level = level - @_logger.datetime_format = '%a %d-%m-%Y %H%M ' - @_logger.info("Logger has been created") - @_logger - end - - def self.logger - @_logger - end -end \ No newline at end of file diff --git a/devops-service/exceptions/conflict_exception.rb b/devops-service/exceptions/conflict_exception.rb new file mode 100644 index 0000000..a4e78c2 --- /dev/null +++ b/devops-service/exceptions/conflict_exception.rb @@ -0,0 +1,4 @@ +class ConflictException < Exception#StandardError + +end + diff --git a/devops-service/db/exceptions/invalid_record.rb b/devops-service/exceptions/invalid_record.rb similarity index 100% rename from devops-service/db/exceptions/invalid_record.rb rename to devops-service/exceptions/invalid_record.rb diff --git a/devops-service/db/exceptions/record_not_found.rb b/devops-service/exceptions/record_not_found.rb similarity index 100% rename from devops-service/db/exceptions/record_not_found.rb rename to devops-service/exceptions/record_not_found.rb diff --git a/devops-service/routes/v2.0/deploy.rb b/devops-service/routes/v2.0/deploy.rb deleted file mode 100644 index ea881a3..0000000 --- a/devops-service/routes/v2.0/deploy.rb +++ /dev/null @@ -1,31 +0,0 @@ - -module Devops - module Version2_0 - module Routes - module DeployRoutes - - def self.registered(app) - - # Run chef-client on reserved server - # - # * *Request* - # - method : POST - # - headers : - # - Content-Type: application/json - # - body : - # { - # "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 - # } - # - # * *Returns* : text stream - app.post_with_headers "/deploy", :headers => [:content_type], &Devops::Version2_0::Handler::Deploy.deploy - - puts "Deploy routes initialized" - end - - end - end - end -end diff --git a/devops-service/routes/v2.0/handlers/bootstrap_templates.rb b/devops-service/routes/v2.0/handlers/bootstrap_templates.rb deleted file mode 100644 index 90b3cbd..0000000 --- a/devops-service/routes/v2.0/handlers/bootstrap_templates.rb +++ /dev/null @@ -1,20 +0,0 @@ -require "commands/bootstrap_templates" - -module Devops - module Version2_0 - module Handler - class BootstrapTemplates - extend BootstrapTemplatesCommands - - def self.get_bootstrap_templates - lambda { - check_privileges("templates", "r") -# broadcast(:cancel_order_failed, "hello") - json BootstrapTemplates.get_templates - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/deploy.rb b/devops-service/routes/v2.0/handlers/deploy.rb deleted file mode 100644 index 9efc066..0000000 --- a/devops-service/routes/v2.0/handlers/deploy.rb +++ /dev/null @@ -1,70 +0,0 @@ -require "commands/deploy" -require "commands/status" -require "workers/deploy_worker" - -module Devops - module Version2_0 - module Handler - class Deploy - extend DeployCommands - extend StatusCommands - - def self.deploy - lambda { - 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) || [] - - servers = settings.mongo.servers(nil, nil, names, true) - halt(404, "No reserved servers found for names '#{names.join("', '")}'") if servers.empty? - keys = {} - servers.sort_by!{|s| names.index(s.chef_node_name)} - if r.key?("trace") - stream() do |out| - status = [] - begin - servers.each do |s| - project = begin - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - rescue InvalidPrivileges, RecordNotFound => e - out << e.message + "\n" - status.push 2 - next - end - res = deploy_server_proc.call(out, s, settings.mongo, tags) - status.push(res) - end - out << create_status(status) - rescue IOError => e - logger.error e.message - break - end - end # stream - else - dir = DevopsService.config[:report_dir_v2] - files = [] - uri = URI.parse(request.url) - servers.each do |s| - project = begin - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - rescue InvalidPrivileges, RecordNotFound => e - next - end - jid = DeployWorker.perform_async(dir, s.to_hash, tags, request.env['REMOTE_USER'], DevopsService.config) - logger.info "Job '#{jid}' has been started" - uri.path = "#{DevopsService.config[:url_prefix]}/v2.0/report/" + jid - files.push uri.to_s - end - sleep 1 - json files - end - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/filter.rb b/devops-service/routes/v2.0/handlers/filter.rb deleted file mode 100644 index e765490..0000000 --- a/devops-service/routes/v2.0/handlers/filter.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Devops - module Version2_0 - module Handler - class Filter - - def self.get_filters - lambda { - check_privileges("filter", "r") - check_provider(params[:provider]) - json settings.mongo.available_images(params[:provider]) - } - end - - def self.add_filter - lambda { - create_response("Updated", {:images => settings.mongo.add_available_images(@images, params[:provider])}) - } - end - - def self.delete_filter - lambda { - create_response("Deleted", {:images => settings.mongo.delete_available_images(@images, params[:provider])}) - } - end - - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/flavor.rb b/devops-service/routes/v2.0/handlers/flavor.rb deleted file mode 100644 index c71b2d1..0000000 --- a/devops-service/routes/v2.0/handlers/flavor.rb +++ /dev/null @@ -1,19 +0,0 @@ -require "providers/provider_factory" - -module Devops - module Version2_0 - module Handler - class Flavor - def self.get_flavors - lambda { - check_privileges("flavor", "r") - check_provider(params[:provider]) - p = ::Provider::ProviderFactory.get params[:provider] - json p.flavors - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/group.rb b/devops-service/routes/v2.0/handlers/group.rb deleted file mode 100644 index eca44de..0000000 --- a/devops-service/routes/v2.0/handlers/group.rb +++ /dev/null @@ -1,19 +0,0 @@ -require "providers/provider_factory" - -module Devops - module Version2_0 - module Handler - class Group - def self.get_groups - lambda { - check_privileges("group", "r") - check_provider(params[:provider]) - p = ::Provider::ProviderFactory.get params[:provider] - json p.groups(params) - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/image.rb b/devops-service/routes/v2.0/handlers/image.rb deleted file mode 100644 index 73c7703..0000000 --- a/devops-service/routes/v2.0/handlers/image.rb +++ /dev/null @@ -1,75 +0,0 @@ -require "providers/provider_factory" -require "commands/image" - -module Devops - module Version2_0 - module Handler - class Image - - extend ImageCommands - - def self.get_images - lambda { - check_privileges("image", "r") - check_provider(params[:provider]) if params[:provider] - images = settings.mongo.images(params[:provider]) - json(images.map {|i| i.to_hash}) - } - end - - def self.get_provider_images - lambda { - check_privileges("image", "r") - check_provider(params[:provider]) - json Image.get_available_provider_images(settings.mongo, params[:provider]) - } - end - - def self.get_image - lambda { - check_privileges("image", "r") - json settings.mongo.image(params[:image_id]) - } - end - - def self.create_image - lambda { - check_privileges("image", "w") - image = create_object_from_json_body - settings.mongo.image_insert Devops::Model::Image.new(image) - create_response "Created", nil, 201 - } - end - - def self.update_image - lambda { - check_privileges("image", "w") - settings.mongo.image params[:image_id] - image = Devops::Model::Image.new(create_object_from_json_body) - image.id = params[:image_id] - settings.mongo.image_update image - create_response("Image '#{params[:image_id]}' has been updated") - } - end - - def self.delete_image - lambda { - check_privileges("image", "w") - projects = settings.mongo.projects_by_image params[:image_id] - unless projects.empty? - ar = [] - projects.each do |p| - ar += p.deploy_envs.select{|e| e.respond_to?(:image)}.select{|e| e.image == params[:image_id]}.map{|e| "#{p.id}.#{e.identifier}"} - end - raise DependencyError.new "Deleting is forbidden: Image is used in #{ar.join(", ")}" - end - - r = settings.mongo.image_delete params[:image_id] - create_response("Image '#{params[:image_id]}' has been removed") - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/key.rb b/devops-service/routes/v2.0/handlers/key.rb deleted file mode 100644 index fb96bd9..0000000 --- a/devops-service/routes/v2.0/handlers/key.rb +++ /dev/null @@ -1,60 +0,0 @@ -module Devops - module Version2_0 - module Handler - class Key - - def self.get_keys - lambda { - check_privileges("key", "r") - keys = settings.mongo.keys.map {|i| i.to_hash} - keys.each {|k| k.delete("path")} # We should not return path to the key - json keys - } - end - - def self.create_key - lambda { - 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) - File.open(file_name, "w") do |f| - f.write(content) - f.chmod(0400) - end - - key = Devops::Model::Key.new({"path" => file_name, "id" => kname}) - settings.mongo.key_insert key - create_response("Created", nil, 201) - } - end - - def self.delete_key - lambda { - check_privileges("key", "w") - servers = settings.mongo.servers_by_key params[:key] - 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 = settings.mongo.key params[:key] - begin - FileUtils.rm(k.path) - rescue - logger.error "Missing key file for #{params[:key]} - #{k.filename}" - end - r = settings.mongo.key_delete params[:key] - return [500, r["err"].inspect] unless r["err"].nil? - create_response("Key '#{params[:key]}' removed") - } - end - - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/network.rb b/devops-service/routes/v2.0/handlers/network.rb deleted file mode 100644 index c9b67ed..0000000 --- a/devops-service/routes/v2.0/handlers/network.rb +++ /dev/null @@ -1,20 +0,0 @@ -require "providers/provider_factory" - -module Devops - module Version2_0 - module Handler - class Network - - def self.get_networks - lambda { - check_privileges("network", "r") - check_provider(params[:provider]) - p = ::Provider::ProviderFactory.get params[:provider] - json p.networks_detail - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/project.rb b/devops-service/routes/v2.0/handlers/project.rb deleted file mode 100644 index de941a6..0000000 --- a/devops-service/routes/v2.0/handlers/project.rb +++ /dev/null @@ -1,315 +0,0 @@ -require "commands/deploy" -require "commands/status" -require "commands/server" -require "db/mongo/models/project" -require "workers/project_test_worker" - -module Devops - module Version2_0 - module Handler - class Project - - extend DeployCommands - extend StatusCommands - extend ServerCommands - - def self.get_projects - lambda { - check_privileges("project", "r") - 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") - json settings.mongo.projects(nil, nil, fields, archived).map {|p| p.to_hash} - } - end - - def self.get_project - lambda { - check_privileges("project", "r") - json settings.mongo.project(params[:project]) - } - end - - def self.get_project_servers - lambda { - check_privileges("project", "r") - settings.mongo.project(params[:project]) - json settings.mongo.servers(params[:project], params[:deploy_env]).map{|s| s.to_hash} - } - end - - def self.get_project_stacks - lambda { - check_privileges("project", "r") - settings.mongo.project(params[:project]) - options = {project: params[:project]} - options[:deploy_env] = params[:deploy_env] if params[:deploy_env] - json settings.mongo.stacks(options).map{|s| s.to_hash} - } - end - - # TODO: multi project - def self.create_project - lambda { - 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) - p = Devops::Model::Project.new(body) - halt_response("Project '#{p.id}' already exist") if settings.mongo.is_project_exists?(p) - p.add_authorized_user [request.env['REMOTE_USER']] - p.create - roles_res = "" - if p.multi? - logger.info "Project '#{p.id}' with type 'multi' created" - else - logger.info "Project '#{p.id}' created" - roles = Project.create_roles p.id, p.deploy_envs, logger - roles_res = ". " + Project.create_roles_response(roles) - end - res = "Created" + roles_res - create_response(res, nil, 201) - } - end - - # TODO: multi project - def self.update_project - lambda { - check_privileges("project", "w") - project = Devops::Model::Project.new(create_object_from_json_body) - project.id = params[:project] - old_project = settings.mongo.project params[:project] - settings.mongo.project_update project - roles = Devops::Version2_0::Handler::Project.create_new_roles(old_project, project, logger) - info = "Project '#{project.id}' has been updated." + Project.create_roles_response(roles) - create_response(info) - } - end - - # TODO: multi project - def self.update_project_users - lambda { - 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) - project = settings.mongo.project(params[:id]) - users = settings.mongo.users(users).map{|u| u.id} - buf = users - users - project.add_authorized_user users, deploy_env - settings.mongo.project_update(project) - info = "Users '#{users.join("', '")}' have been added to '#{params[:id]}' project's authorized users" - info << ", invalid users: '#{buf.join("', '")}'" unless buf.empty? - create_response(info) - } - end - - # TODO: multi project - def self.delete_project_users - lambda { - check_privileges("project", "w") - @project.remove_authorized_user @users, @deploy_env - settings.mongo.project_update @project - info = "Users '#{@users.join("', '")}' have been removed from '#{params[:id]}' project's authorized users" - create_response(info) - } - end - - # TODO: multi project - def self.set_project_env_run_list - lambda { - check_privileges("project", "w") - list = create_object_from_json_body(Array) - check_array(list, "Body must contains not empty array of strings") - project = settings.mongo.project(params[:id]) - env = project.deploy_env params[:env] - env.run_list = list - settings.mongo.project_update project - create_response("Updated environment '#{env.identifier}' with run_list '#{env.run_list.inspect}' in project '#{project.id}'") - } - end - - def self.delete_project - lambda { - check_privileges("project", "w") - servers = settings.mongo.servers params[:project] - raise DependencyError.new "Deleting #{params[:project]} is forbidden: Project has servers" if !servers.empty? - 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 - project = settings.mongo.project(params[:project]) - info = if deploy_env.nil? - project.delete - "Project '#{params[:project]}' is deleted" - else - project.remove_env deploy_env - settings.mongo.project_update project - "Project '#{params[:project]}'. Deploy environment '#{deploy_env}' has been deleted" - end - create_response(info) - } - end - - def self.deploy_project - lambda { - check_privileges("project", "x") - obj = create_object_from_json_body - check_string(obj["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true) - check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true) - project = settings.mongo.project(params[:id]) - servers = settings.mongo.servers(params[:id], obj["deploy_env"], obj["servers"], true) - keys = {} - if obj.key?("trace") - stream() do |out| - begin - out << (servers.empty? ? "No reserved servers to deploy\n" : "Deploy servers: '#{servers.map{|s| s.chef_node_name}.join("', '")}'\n") - status = [] - servers.each do |s| - logger.debug "Deploy server: #{s.inspect}" - - begin - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - rescue InvalidPrivileges, RecordNotFound => e - out << e.message + "\n" - status.push 2 - next - end - unless keys.key? s.key - k = settings.mongo.key s.key - keys[s.key] = k.path - end - status.push(deploy_server(out, s, keys[s.key])) - end - out << create_status(status) - rescue IOError => e - logger.error e.message - end - end - else - dir = DevopsConfig[:report_dir_v2] - files = [] - uri = URI.parse(request.url) - servers.each do |s| - project = begin - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - rescue InvalidPrivileges, RecordNotFound => e - next - end - jid = DeployWorker.perform_async(dir, s.to_hash, [], DevopsConfig.config) - logger.info "Job '#{jid}' has been started" - uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid - files.push uri.to_s - end - json files - end - } - end - - def self.archive_project - lambda { - check_privileges("project", "w") - project = settings.mongo.project(params[:project]) - if project.nil? - create_response("Project '#{params[:project]}' not found", nil, 404) - else - settings.mongo.archive_project(params[:project]) - info = "Project '#{params[:project]}' has been archived" - create_response(info) - end - } - end - - def self.unarchive_project - lambda { - check_privileges("project", "w") - project = settings.mongo.project(params[:project]) - if project.nil? - create_response("Project '#{params[:project]}' not found", nil, 404) - else - settings.mongo.unarchive_project(params[:project]) - info = "Project '#{params[:project]}' has been unarchived" - create_response(info) - end - } - end - - def self.test_project - lambda { - check_privileges("project", "r") - project = settings.mongo.project(params[:id]) - env = project.deploy_env params[:env] - 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 - return [400, msg] - end - - dir = DevopsConfig[:report_dir_v2] - uri = URI.parse(request.url) - p = { - :project => project.id, - :env => env.identifier, - :user => request.env['REMOTE_USER'] - } - jid = ProjectTestWorker.perform_async(dir, p, DevopsConfig.config) - Worker.set_status jid, Worker::STATUS::IN_QUEUE - logger.info "Job '#{jid}' has been created" - uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid - files = [uri.to_s] - sleep 1 - json files - } - end - - def self.create_roles project_id, envs, logger - all_roles = KnifeCommands.roles - return " Can't get roles list" if all_roles.nil? - roles = {:new => [], :error => [], :exist => []} - envs.each do |e| - role_name = KnifeCommands.role_name(project_id, e.identifier) - begin - if all_roles.include? role_name - roles[:exist].push role_name - else - KnifeCommands.create_role role_name, project_id, e.identifier - roles[:new].push role_name - 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}" - end - end - roles - end - - def self.create_new_roles old_project, new_project, logger - old_project.deploy_envs.each do |e| - new_project.remove_env(e.identifier) - end - Devops::Version2_0::Handler::Project.create_roles new_project.id, new_project.deploy_envs, logger - end - - def self.create_roles_response roles - if roles.is_a?(String) - roles - else - info = "" - info += " Project roles '#{roles[:new].join("', '")}' have been automaticaly created" unless roles[:new].empty? - info += " Project roles '#{roles[:exist].join("', '")}' weren't created because they exist" unless roles[:exist].empty? - info += " Project roles '#{roles[:error].join("', '")}' weren't created because of internal error" unless roles[:error].empty? - info - end - end - - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/provider.rb b/devops-service/routes/v2.0/handlers/provider.rb deleted file mode 100644 index a777dc6..0000000 --- a/devops-service/routes/v2.0/handlers/provider.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "providers/provider_factory" - -module Devops - module Version2_0 - module Handler - class Provider - def self.get_providers - lambda { - check_privileges("provider", "r") - json ::Provider::ProviderFactory.providers - } - end - end - end - end -end diff --git a/devops-service/routes/v2.0/handlers/report.rb b/devops-service/routes/v2.0/handlers/report.rb deleted file mode 100644 index 66e2a99..0000000 --- a/devops-service/routes/v2.0/handlers/report.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Devops - module Version2_0 - module Handler - class Report - - def self.reports_all - lambda { - options = {} - ["project", "deploy_env", "type", "created_by", "date_from", "date_to", "sort", "status", "max_number", "chef_node_name"].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] - end - json Devops::Db.connector.reports(options).map{|r| r.to_hash} - } - end - - def self.reports_latest - lambda { - options = {} - ["project", "deploy_env", "type", "created_by", "date_from", "date_to", "sort", "status", "chef_node_name"].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] - end - json Devops::Db.connector.latest_reports(options).map{|r| r.to_hash} - } - end - - def self.attributes_all - lambda{ - json Devops::Db.connector.reports_attributes_values(params["name"]) - } - end - - def self.report - lambda{ - begin - r = Devops::Db.connector.report(params[:id]) - file = r.file - return [404, "Report '#{params[:id]}' does not exist"] unless File.exists? file - @text = Rack::Utils.escape_html(File.read(file)) - @done = completed?(params[:id]) - rescue RecordNotFound => e - if task_status(params[:id]) == Worker::STATUS::IN_QUEUE - @text = "Task '#{params[:id]}' has been queued" - @done = false - else - raise e - end - end - erb :index - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/script.rb b/devops-service/routes/v2.0/handlers/script.rb deleted file mode 100644 index 9887063..0000000 --- a/devops-service/routes/v2.0/handlers/script.rb +++ /dev/null @@ -1,127 +0,0 @@ -require "providers/provider_factory" -require "fileutils" -require "commands/status" - -module Devops - module Version2_0 - module Handler - class Script - - def self.get_scripts - lambda { - check_privileges("script", "r") - res = [] - Dir.foreach(DevopsService.config[:scripts_dir]) {|f| res.push(f) unless f.start_with?(".")} - json res - } - end - - def self.execute_command - lambda { - check_privileges("script", "x") - user = request.env['REMOTE_USER'] - s = ::Devops::Db.connector.server_by_chef_node_name params[:node_name] - ::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user - cert = ::Devops::Db.connector.key s.key - cmd = request.body.read - addr = "#{s.remote_user}@#{s.public_ip || s.private_ip}" - ssh_cmd = "ssh -i %s #{addr} '#{cmd}'" - stream() do |out| - begin - out << ssh_cmd % File.basename(cert.path) - out << "\n" - IO.popen((ssh_cmd % cert.path) + " 2>&1") do |so| - while line = so.gets do - out << line - end - end - out << "\nDone" - rescue IOError => e - logger.error e.message - end - end - } - end - - def self.run_script - lambda { - check_privileges("script", "x") - file_name = params[:script_name] - @file = File.join(DevopsService.config[:scripts_dir], check_filename(file_name, "Parameter 'script_name' must be a not empty string", false)) - halt(404, "File '#{file_name}' does not exist") unless File.exists?(@file) - 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) - servers = ::Devops::Db.connector.servers_by_names(nodes) - return [404, "No servers found for names '#{nodes.join("', '")}'"] if servers.empty? - user = request.env['REMOTE_USER'] - servers.each do |s| - ::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user - end - stream() do |out| - begin - 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 p.nil? - ssh_cmd += " " + p.join(" ") - end - out << (ssh_cmd % [params[:script_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 - end - rescue IOError => e - logger.error e.message - out << e.message - status.push 3 - end - end - out << create_status(status) - rescue IOError => e - logger.error e.message - end - end - } - end - - def self.create_script - lambda { - check_privileges("script", "w") - file_name = params[:script_name] - file = File.join(settings.scripts_dir, check_filename(file_name, "Parameter 'script_name' must be a not empty string")) - halt_response("File '#{file_name}' already exist") if File.exists?(file) - File.open(file, "w") {|f| f.write(request.body.read)} - create_response("File '#{params[:script_name]}' created", nil, 201) - } - end - - def self.delete_script - lambda { - check_privileges("script", "w") - file_name = params[:script_name] - file = File.join(settings.scripts_dir, check_filename(file_name, "Parameter 'script_name' must be a not empty string")) - halt_response("File '#{file_name}' does not exist", 404) unless File.exists?(file) - FileUtils.rm(file) - create_response("File '#{params[:script_name]}' deleted") - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/server.rb b/devops-service/routes/v2.0/handlers/server.rb deleted file mode 100644 index 800d7d2..0000000 --- a/devops-service/routes/v2.0/handlers/server.rb +++ /dev/null @@ -1,349 +0,0 @@ -require 'rufus-scheduler' -require "uri" - -require "commands/status" -require "commands/server" -require "commands/bootstrap_templates" -require "commands/knife_commands" - -require "providers/provider_factory" - -require "db/mongo/models/server" - -require "workers/create_server_worker" -require "workers/bootstrap_worker" - -module Devops - module Version2_0 - module Handler - class Server - - extend StatusCommands - extend ServerCommands - extend BootstrapTemplatesCommands - - scheduler = Rufus::Scheduler.new - - def self.get_servers - lambda { - 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 settings.mongo.servers(nil, nil, nil, reserved, fields).map {|s| s.to_hash} - } - end - - def self.get_chef_servers - lambda { - check_privileges("server", "r") - json KnifeCommands.chef_node_list - } - end - - def self.get_provider_servers - lambda { - check_privileges("server", "r") - json ::Provider::ProviderFactory.get(params[:provider]).servers - } - end - - def self.get_server - lambda { - check_privileges("server", "r") - json Server.get_server_by_key(params[:name], params[:key]).to_hash - } - end - - def self.delete_server - lambda { - check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - s = Server.get_server_by_key(params[:id], key) - ### Authorization - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - info, r = delete_server(s, settings.mongo, logger) - create_response(info, r) - } - end - - def self.create_server - lambda { - check_privileges("server", "w") - body = create_object_from_json_body - user = request.env['REMOTE_USER'] - 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"] - halt_response("Parameter 'without_bootstrap' should be a null or true") unless without_bootstrap.nil? or without_bootstrap == true - force = body["force"] - 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) - new_key = settings.mongo.key(key_name) unless key_name.nil? - - p = settings.mongo.check_project_auth(project_name, env_name, user) - env = p.deploy_env(env_name) - - provider = ::Provider::ProviderFactory.get(env.provider) - Server.check_chef_node_name(server_name, provider) unless server_name.nil? - unless groups.nil? - buf = groups - provider.groups.keys - halt_response("Invalid security groups '#{buf.join("', '")}' for provider '#{provider.name}'") if buf.empty? - end - - servers = Server.extract_servers(provider, p, env, body, user, settings.mongo) - if body.key?("trace") - stream() do |out| - begin - status = [] - servers.each do |s| - res = create_server_proc.call(out, s, provider, settings.mongo) - status.push res - end - out << create_status(status) - rescue IOError => e - logger.error e.message - end - end - else - dir = DevopsConfig[:report_dir_v2] - files = [] - uri = URI.parse(request.url) - servers.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) - Worker.set_status jid, Worker::STATUS::IN_QUEUE - logger.info "Job '#{jid}' has been started" - uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid - files.push uri.to_s - end - sleep 1 - json files - end - } - end - - def self.pause_server - lambda { - check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - s = Server.get_server_by_key(params[:node_name], key) - ## Authorization - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - provider = ::Provider::ProviderFactory.get(s.provider) - r = provider.pause_server s - if r.nil? - create_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' is paused") - else - halt_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' can not be paused, It in state '#{r}'", 409) - end - } - end - - def self.unpause_server - lambda { - check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - s = Server.get_server_by_key(params[:node_name], key) - ## Authorization - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - provider = ::Provider::ProviderFactory.get(s.provider) - r = provider.unpause_server s - if r.nil? - create_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' is unpaused") - else - halt_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' can not be unpaused, It in state '#{r}'", 409) - end - } - end - - def self.reserve_server - lambda { - check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - s = Server.get_server_by_key(params[:node_name], key) - user = request.env['REMOTE_USER'] - settings.mongo.check_project_auth s.project, s.deploy_env, user - halt_response(400, "Server '#{params[:node_name]}' already reserved") unless s.reserved_by.nil? - s.reserved_by = user - settings.mongo.server_update(s) - create_response("Server '#{params[:node_name]}' has been reserved") - } - end - - def self.unreserve_server - lambda { - check_privileges("server", "w") - body = create_object_from_json_body(Hash, true) - key = (body.nil? ? nil : body["key"]) - s = Server.get_server_by_key(params[:node_name], key) - settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - halt_response(400, "Server '#{params[:node_name]}' is not reserved") if s.reserved_by.nil? - s.reserved_by = nil - settings.mongo.server_update(s) - create_response("Server '#{params[:node_name]}' has been unreserved") - } - end - - # TODO: check bootstrap template name - def self.bootstrap_server - lambda { - 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) - s = settings.mongo.server_by_instance_id(id) - - p = settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] - d = p.deploy_env s.deploy_env - - provider = ::Provider::ProviderFactory.get(s.provider) - - Server.check_chef_node_name(name, provider) unless name.nil? - s.options = { - :run_list => rl || d.run_list, - } - unless t.nil? - templates = get_templates - halt_response("Invalid bootstrap template '#{t}', available values: #{templates.join(", ")}", 400) unless templates.include?(t) - s.options[:bootstrap_template] = t - end - s.chef_node_name = name || provider.create_default_chef_node_name(s) - logger.debug "Chef node name: '#{s.chef_node_name}'" - status = [] - if body.key?("trace") - stream() do |out| - begin - cert = settings.mongo.key s.key - logger.debug "Bootstrap certificate path: #{cert.path}" - bootstrap s, out, cert.path, logger - str = nil - r = if check_server(s) - settings.mongo.server_set_chef_node_name s - str = "Server with id '#{s.id}' is bootstraped" - logger.info str - 0 - else - str = "Server with id '#{s.id}' is not bootstraped" - logger.warn str - 1 - end - status.push r - out << str - out << "\n" - out << create_status(status) - rescue IOError => e - logger.error e.message - end - end - else - 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) - Worker.set_status jid, Worker::STATUS::IN_QUEUE - logger.info "Job '#{jid}' has been started" - uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid - uri.query = nil - uri.fragment = nil - files.push uri.to_s - sleep 1 - json files - end - } - end - - def self.add_server - lambda { - 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) - p = settings.mongo.check_project_auth project, deploy_env, request.env['REMOTE_USER'] - - d = p.deploy_env(deploy_env) - - cert = settings.mongo.key(key) - provider = ::Provider::ProviderFactory.get("static") - s = Devops::Model::Server.new - s.provider = provider.name - s.project = project - s.deploy_env = deploy_env - s.remote_user = remote_user - s.private_ip = private_ip - s.public_ip = public_ip - s.static = true - s.id = "static_#{cert.id}-#{Time.now.to_i}" - s.key = cert.id - settings.mongo.server_insert s - create_response("Server '#{s.id}' has been added") - } - end - - def self.get_server_by_key id, key - mongo = Devops::Db.connector - key == "instance" ? mongo.server_by_instance_id(id) : mongo.server_by_chef_node_name(id) - end - - def self.check_chef_node_name name, provider - mongo = Devops::Db.connector - mongo.server_by_chef_node_name name - halt(400, "Server with name '#{name}' already exist") - rescue RecordNotFound => e - # server not found - OK - s = provider.servers.detect {|s| s["name"] == name} - halt(400, "#{provider.name} node with name '#{name}' already exist") unless s.nil? - s = KnifeCommands.chef_node_list.detect {|n| n == name} - halt(400, "Chef node with name '#{name}' already exist") unless s.nil? - s = KnifeCommands.chef_client_list.detect {|c| c == name} - halt(400, "Chef client with name '#{name}' already exist") unless s.nil? - end - - end - - class ExpireHandler - include ServerCommands - - def initialize server, logger - @server = server - @logger = logger - end - - def call(job) - @logger.info("Removing node '#{@server.chef_node_name}' form project '#{@server.project}' and env '#{@server.deploy_env}'") - begin - delete_server(@server, Devops::Db.connector, @logger) - rescue => e - logger.error "ExpiredHandler error: " + e.message - end - end - end - - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/stack.rb b/devops-service/routes/v2.0/handlers/stack.rb deleted file mode 100644 index 5dc410e..0000000 --- a/devops-service/routes/v2.0/handlers/stack.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'db/mongo/models/stack/stack_factory' - -module Devops - module Version2_0 - module Handler - class Stack - - def self.get_stacks - lambda { - check_privileges("stack", "r") - stacks = settings.mongo.stacks - json stacks.map(&:to_hash) - } - end - - def self.get_stacks_for_provider - lambda { - check_privileges("stack", "r") - check_provider(params[:provider]) - stacks = settings.mongo.stacks(provider: params[:provider]) - json stacks.map(&:to_hash) - } - end - - def self.create_stack - lambda { - check_privileges("stack", "w") - - object = create_object_from_json_body - stack_model = Model::StackFactory.create(object['provider'], object) - settings.mongo.stack_insert(stack_model) - - create_response "Created", stack_model.to_hash, 201 - } - end - - def self.get_stack - lambda { - check_privileges("stack", "r") - stack = settings.mongo.stack(params[:stack_id]) - json stack.to_hash - } - end - - def self.delete_stack - lambda { - check_privileges("stack", "w") - - stack = settings.mongo.stack(params[:stack_id]) - stack.delete_stack_in_cloud! - settings.mongo.stack_delete(params[:stack_id]) - - create_response("Stack '#{params[:stack_id]}' has been removed") - } - end - - def self.sync_details - lambda { - check_privileges("stack", "w") - - stack = settings.mongo.stack(params[:stack_id]) - stack.sync_details! - settings.mongo.stack_update(stack) - - json stack.to_hash - } - end - - def self.resources - lambda { - check_privileges("stack", "r") - stack = settings.mongo.stack(params[:stack_id]) - json stack.resources - } - end - - def self.resource - lambda { - check_privileges("stack", "r") - stack = settings.mongo.stack(params[:stack_id]) - json stack.resource(params[:resource_id]) - } - end - - end - end - end -end \ No newline at end of file diff --git a/devops-service/routes/v2.0/handlers/stack_preset.rb b/devops-service/routes/v2.0/handlers/stack_preset.rb deleted file mode 100644 index 2cf3953..0000000 --- a/devops-service/routes/v2.0/handlers/stack_preset.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'json' -require 'lib/stack_presets/factory' -require 'workers/stack_sync_worker' -require 'workers/job_starter' - -module Devops - module Version2_0 - module Handler - class StackPreset - - def self.get_presets - lambda { - # check_privileges("stack_presets", "r") - json Devops::StackPresetsFactory.list.map(&:to_hash) - } - end - - def self.get_preset - lambda { - # check_privileges("stack_presets", "r") - json Devops::StackPresetsFactory.get(params['id']).to_hash - } - end - - def self.apply - lambda { - # check_privileges("stack_presets", "r") - check_privileges('stack_template', 'w') - - attrs = create_object_from_json_body - preset = Devops::StackPresetsFactory.get(attrs.fetch('id')) - stack = preset.create_stack_from_preset(attrs) - settings.mongo.stack_insert(stack) - - file = JobStarter.start_job(:worker, :sync_stack_till_not_in_progress, - provider: stack.provider, - stack_id: stack.id, - request: request - ) - - puts "Syncing report is located here: #{file}" - - create_response 'Created', stack.to_hash, 201 - } - end - - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/stack_template.rb b/devops-service/routes/v2.0/handlers/stack_template.rb deleted file mode 100644 index ae8e799..0000000 --- a/devops-service/routes/v2.0/handlers/stack_template.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'db/mongo/models/stack_template/stack_template_factory' - -module Devops - module Version2_0 - module Handler - class StackTemplate - - def self.get_stack_templates - lambda { - check_privileges('stack_template', 'r') - stack_templates = settings.mongo.stack_templates - json stack_templates.map(&:to_hash) - } - end - - def self.get_stack_templates_for_provider - lambda { - check_privileges('stack_template', 'r') - check_provider(params[:provider]) - stack_templates = settings.mongo.stack_templates(params[:provider]) - json stack_templates.map(&:to_hash) - } - end - - def self.create_stack_template - lambda { - check_privileges('stack_template', 'w') - - attrs = create_object_from_json_body - template_model = Model::StackTemplateFactory.create(attrs['provider'], attrs) - settings.mongo.stack_template_insert(template_model) - - create_response 'Created', template_model.to_hash, 201 - } - end - - def self.get_stack_template - lambda { - check_privileges('stack_template', 'r') - stack_template = settings.mongo.stack_template(params[:stack_template_id]) - json stack_template.to_hash - } - end - - def self.delete_stack_template - lambda { - check_privileges('stack_template', 'w') - - settings.mongo.stack_template_delete params[:stack_template_id] - create_response("Template '#{params[:stack_template_id]}' has been removed") - } - end - - end - end - end -end \ No newline at end of file diff --git a/devops-service/routes/v2.0/handlers/status.rb b/devops-service/routes/v2.0/handlers/status.rb deleted file mode 100644 index 0f2eab2..0000000 --- a/devops-service/routes/v2.0/handlers/status.rb +++ /dev/null @@ -1,22 +0,0 @@ -require "sidekiq" - -module Devops - module Version2_0 - module Handler - class Status - - def self.get_status - lambda { - r = Sidekiq.redis do |connection| - connection.hget("devops", params[:id]) - end - return [404, "Job with id '#{params[:id]}' not found"] if r.nil? - r - } - end - - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/tag.rb b/devops-service/routes/v2.0/handlers/tag.rb deleted file mode 100644 index 9198aac..0000000 --- a/devops-service/routes/v2.0/handlers/tag.rb +++ /dev/null @@ -1,51 +0,0 @@ -require "commands/knife_commands" - -module Devops - module Version2_0 - module Handler - class Tag - - def self.get_tags - lambda { - check_privileges("server", "r") - server = settings.mongo.server_by_chef_node_name(params[:node_name]) - halt_response("No servers found for name '#{params[:node_name]}'", 404) if server.nil? - chef_node_name = server.chef_node_name - json(KnifeCommands.tags_list(chef_node_name)) - } - end - - def self.set_tags - lambda { - check_privileges("server", "w") - tags = create_object_from_json_body(Array) - check_array(tags, "Request body should be a not empty array of strings") - server = settings.mongo.server_by_chef_node_name(params[:node_name]) - halt_response("No servers found for name '#{params[:node_name]}'", 404) if server.nil? - chef_node_name = server.chef_node_name - tagsStr = tags.join(" ") - cmd = KnifeCommands.tags_create(chef_node_name, tagsStr) - halt_response("Error: Cannot add tags #{tagsStr} to server #{chef_node_name}", 500) unless cmd[1] - create_response("Set tags for #{chef_node_name}: #{tagsStr}") - } - end - - def self.unset_tags - lambda { - check_privileges("server", "w") - tags = create_object_from_json_body(Array) - check_array(tags, "Request body should be a not empty array of strings") - server = settings.mongo.server_by_chef_node_name(params[:node_name]) - halt_response("No servers found for name '#{params[:node_name]}'", 404) if server.nil? - chef_node_name = server.chef_node_name - tagsStr = tags.join(" ") - cmd = KnifeCommands.tags_delete(chef_node_name, tagsStr) - halt_response("Cannot delete tags #{tagsStr} from server #{chef_node_name}: #{cmd[0]}", 500) unless cmd[1] - create_response("Deleted tags for #{chef_node_name}: #{tagsStr}") - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/handlers/user.rb b/devops-service/routes/v2.0/handlers/user.rb deleted file mode 100644 index 2a8da88..0000000 --- a/devops-service/routes/v2.0/handlers/user.rb +++ /dev/null @@ -1,85 +0,0 @@ -require "db/exceptions/invalid_record" -require "db/mongo/models/user" - -module Devops - module Version2_0 - module Handler - class User - - def self.get_users - lambda { - check_privileges("user", "r") - users = Devops::Db.connector.users.map {|i| i.to_hash} - users.each {|u| u.delete("password")} - json users - } - end - - def self.create_user - lambda { - 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::Db.connector.user_insert Devops::Model::User.new(user) - create_response("Created", nil, 201) - } - end - - def self.delete_user - lambda { - 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 - - r = Devops::Db.connector.user_delete params[:user] - create_response("User '#{params[:user]}' removed") - } - end - - def self.change_user_privileges - lambda { - check_privileges("user", "w") - data = create_object_from_json_body - user = Devops::Db.connector.user params[:user] - 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) || "" - user.grant(cmd, privileges) - Devops::Db.connector.user_update user - create_response("Updated") - } - end - - def self.change_user_email_or_password - lambda { - 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 - - 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") - user = Devops::Db.connector.user u - user.send("#{action}=", p) - Devops::Db.connector.user_update user - create_response("Updated") - } - end - end - end - end -end - diff --git a/devops-service/routes/v2.0/key.rb b/devops-service/routes/v2.0/key.rb deleted file mode 100644 index 956b25f..0000000 --- a/devops-service/routes/v2.0/key.rb +++ /dev/null @@ -1,63 +0,0 @@ -require "json" -require "db/exceptions/invalid_record" -require "db/mongo/models/key" -require "fileutils" - -module Devops - module Version2_0 - module Routes - module KeyRoutes - - def self.registered(app) - # Get list of available ssh keys - # - # * *Request* - # - method : GET - # - headers : - # - Accept: application/json - # - # * *Returns* : array of strings - # [ - # { - # "scope": "system", -> 'system' - key was added by server, 'user' - key was added by user - # "id": "devops" - # } - # ] - app.get_with_headers "/keys", :headers => [:accept], &Devops::Version2_0::Handler::Key.get_keys - - # Create ssh key on devops server - # - # * *Request* - # - method : POST - # - headers : - # - Accept: application/json - # - Content-Type: application/json - # - body : - # { - # "file_name": "key file name", - # "key_name": "key name", - # "content": "key content" - # } - # - # * *Returns* : - # 201 - Created - app.post_with_headers "/key", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Key.create_key - - # Delete ssh key from devops server - # - # * *Request* - # - method : DELETE - # - headers : - # - Accept: application/json - # - # * *Returns* : - # 200 - Deleted - app.delete_with_headers "/key/:key", :headers => [:accept], &Devops::Version2_0::Handler::Key.delete_key - - puts "Key routes initialized" - end - - end - end - end -end diff --git a/devops-service/routes/v2.0/report.rb b/devops-service/routes/v2.0/report.rb deleted file mode 100644 index f9c345c..0000000 --- a/devops-service/routes/v2.0/report.rb +++ /dev/null @@ -1,53 +0,0 @@ -module Devops - module Version2_0 - module Routes - module ReportRoutes - - def self.registered(app) - - app.get_with_headers "/report/all", headers: [:accept], &Devops::Version2_0::Handler::Report.reports_all - app.get_with_headers "/report/all/latest", headers: [:accept], &Devops::Version2_0::Handler::Report.reports_latest - app.get_with_headers "/report/all/attributes/:name", headers: [:accept], &Devops::Version2_0::Handler::Report.attributes_all - app.get_with_headers "/report/:id", headers: [:accept], &Devops::Version2_0::Handler::Report.report - puts "Report routes initialized" - end - - def completed? id - r = task_status(id) - r == "completed" or r == "failed" - end - - def task_status id - r = Sidekiq.redis do |connection| - connection.hget("devops", id) - end - end - - end - end - end -end - -__END__ - -@@ layout - - - <% unless @done %> - - <% end %> - - - <%= yield %> - - - -@@ index -
-<%= @text %> -diff --git a/devops-service/routes/v2.0/script.rb b/devops-service/routes/v2.0/script.rb deleted file mode 100644 index c52f724..0000000 --- a/devops-service/routes/v2.0/script.rb +++ /dev/null @@ -1,77 +0,0 @@ - -module Devops - module Version2_0 - module Routes - module ScriptRoutes - - def self.registered(app) - # Get scripts names - # - # * *Request* - # - method : GET - # - headers : - # - Accept: application/json - # - # * *Returns* : - # [ - # "script_1" - # ] - app.get_with_headers "/scripts", :headers => [:accept], &Devops::Version2_0::Handler::Script.get_scripts - - # Run command on node :node_name - # - # * *Request* - # - method : POST - # - body : - # command to run - # - # * *Returns* : text stream - app.post_with_statistic "/script/command/:node_name", &Devops::Version2_0::Handler::Script.execute_command - - # Run script :script_name on nodes - # - # * *Request* - # - method : POST - # - headers : - # - Content-Type: application/json - # - body : - # { - # "nodes": [], -> array of nodes names - # "params": [] -> array of script arguments - # } - # - # * *Returns* : text stream - app.post_with_headers "/script/run/:script_name", :headers => [:content_type], &Devops::Version2_0::Handler::Script.run_script - - hash = {} - # Create script :script_name - # - # * *Request* - # - method : PUT - # - headers : - # - Accept: application/json - # - body : script content - # - # * *Returns* : - # 201 - Created - hash["PUT"] = Devops::Version2_0::Handler::Script.create_script - - # Delete script :script_name - # - # * *Request* - # - method : Delete - # - headers : - # - Accept: application/json - # - # * *Returns* : - # 200 - Deleted - hash["DELETE"] = Devops::Version2_0::Handler::Script.delete_script - app.multi_routes "/script/:script_name", {:headers => [:accept]}, hash - - puts "Script routes initialized" - end - - end - end - end -end diff --git a/devops-service/routes/v2.0/stack.rb b/devops-service/routes/v2.0/stack.rb deleted file mode 100644 index 8634ae0..0000000 --- a/devops-service/routes/v2.0/stack.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Devops - module Version2_0 - module Routes - module StackRoutes - - def self.registered(app) - app.after %r{\A/stack_template(/[\w]+)?\z} do - statistic - end - - app.get_with_headers '/stacks', :headers => [:accept], &Devops::Version2_0::Handler::Stack.get_stacks - - app.get_with_headers '/stacks/provider/:provider', :headers => [:accept], &Devops::Version2_0::Handler::Stack.get_stacks_for_provider - - app.post_with_headers "/stack", :headers => [:accept], &Devops::Version2_0::Handler::Stack.create_stack - - app.post_with_headers "/stack/:stack_id/sync_details", :headers => [:accept], &Devops::Version2_0::Handler::Stack.sync_details - - app.get_with_headers "/stack/:stack_id/resources", :headers => [:accept], &Devops::Version2_0::Handler::Stack.resources - - app.get_with_headers "/stack/:stack_id/resources/:resource_id", :headers => [:accept], &Devops::Version2_0::Handler::Stack.resource - - hash = {} - - hash['GET'] = Devops::Version2_0::Handler::Stack.get_stack - - hash['DELETE'] = Devops::Version2_0::Handler::Stack.delete_stack - - app.multi_routes '/stack/:stack_id', {}, hash - - puts "Stack routes initialized" - end - - end - end - end -end \ No newline at end of file diff --git a/devops-service/routes/v2.0/stack_template.rb b/devops-service/routes/v2.0/stack_template.rb deleted file mode 100644 index abf5488..0000000 --- a/devops-service/routes/v2.0/stack_template.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Devops - module Version2_0 - module Routes - module StackTemplateRoutes - - def self.registered(app) - app.after %r{\A/stack_template(/[\w]+)?\z} do - statistic - end - - app.get_with_headers '/stack_templates', :headers => [:accept], &Devops::Version2_0::Handler::StackTemplate.get_stack_templates - - app.get_with_headers '/stack_templates/provider/:provider', :headers => [:accept], &Devops::Version2_0::Handler::StackTemplate.get_stack_templates_for_provider - - app.post_with_headers "/stack_template", :headers => [:accept], &Devops::Version2_0::Handler::StackTemplate.create_stack_template - - hash = {} - - hash['GET'] = Devops::Version2_0::Handler::StackTemplate.get_stack_template - - hash['DELETE'] = Devops::Version2_0::Handler::StackTemplate.delete_stack_template - - app.multi_routes '/stack_template/:stack_template_id', {}, hash - - puts "Stack_template routes initialized" - end - - end - end - end -end \ No newline at end of file diff --git a/devops-service/routes/v2.0/status.rb b/devops-service/routes/v2.0/status.rb deleted file mode 100644 index 69afa83..0000000 --- a/devops-service/routes/v2.0/status.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Devops - module Version2_0 - module Routes - module StatusRoutes - - def self.registered(app) - app.get "/status/:id", &Devops::Version2_0::Handler::Status.get_status - - puts "Status routes initialized" - end - - end - end - end -end diff --git a/devops-service/tests/features/step_definitions/http_queries_steps.rb b/devops-service/tests/features/step_definitions/http_queries_steps.rb index 14321ae..4720a95 100644 --- a/devops-service/tests/features/step_definitions/http_queries_steps.rb +++ b/devops-service/tests/features/step_definitions/http_queries_steps.rb @@ -31,9 +31,16 @@ When(/^I send POST '(.*)' query with JSON body$/) do |path, body| res = post_body(path, body, DEFAULT_HEADERS) end -When(/^I send POST '(.*)' query with JSON body without header '(.*)'$/) do |path, hs, body| +When(/^I send POST '(.*)' query with body with header 'Accept' value '(.*)'$/) do |path, hv, body| + headers = DEFAULT_HEADERS.clone + headers["Accept"] = hv + res = post_body(path, body, headers) +end + +When(/^I send POST '(.*)' query with JSON body without header 'Content-Type'$/) do |path, body| JSON.parse(body) unless body.strip.empty? - headers = DEFAULT_HEADERS.select{|h, v| h != hs} + headers = DEFAULT_HEADERS.clone + headers.delete("Content-Type") res = post_body(path, body, headers) end @@ -46,20 +53,28 @@ When(/^I send DELETE '(.*)' query$/) do |path| delete(path, {}, DEFAULT_HEADERS) end +When(/^I send DELETE '(.*)' query with JSON body with header 'Accept' value '(.*)'$/) do |path, hv, body| + JSON.parse(body) unless body.strip.empty? + headers = DEFAULT_HEADERS.clone + headers["Accept"] = hv + res = delete_body(path, body, headers) +end + When(/^I send DELETE '(.*)' query with JSON body$/) do |path, body| JSON.parse(body) unless body.strip.empty? res = delete_body(path, body, DEFAULT_HEADERS) end -When(/^I send DELETE '(.*)' query without header '(.*)'$/) do |path, hs| - headers = DEFAULT_HEADERS.select{|h, v| h != hs} - puts headers +When(/^I send DELETE '(.*)' query without header 'Content-Type'$/) do |path, hs| + headers = DEFAULT_HEADERS.clone + headers.delete("Content-Type") res = delete_body(path, nil, headers) end -When(/^I send DELETE '(.*)' query with JSON body without header '(.*)'$/) do |path, hs, body| +When(/^I send DELETE '(.*)' query with JSON body without header 'Content-Type'$/) do |path, body| JSON.parse(body) unless body.strip.empty? - headers = DEFAULT_HEADERS.select{|h, v| h != hs} + headers = DEFAULT_HEADERS.clone + headers.delete("Content-Type") res = delete_body(path, body, headers) end @@ -80,14 +95,23 @@ When(/^I send PUT '(.*)' query with user without privileges$/) do |path| put_without_privileges(path, {}, DEFAULT_HEADERS) end -When(/^I send PUT '(.*)' query with body without header '(.*)'$/) do |path, hs, body| - headers = DEFAULT_HEADERS.select{|h, v| h != hs} +When(/^I send PUT '(.*)' query with JSON body with header 'Accept' value '(.*)'$/) do |path, hv, body| + JSON.parse(body) unless body.strip.empty? + headers = DEFAULT_HEADERS.clone + headers["Accept"] = hv res = put_body(path, body, headers) end -When(/^I send PUT '(.*)' query with JSON body without header '(.*)'$/) do |path, hs, body| +When(/^I send PUT '(.*)' query with body without header 'Content-Type'$/) do |path, body| + headers = DEFAULT_HEADERS.clone + headers.delete("Content-Type") + res = put_body(path, body, headers) +end + +When(/^I send PUT '(.*)' query with JSON body without header 'Content-Type'$/) do |path, body| JSON.parse(body) unless body.strip.empty? - headers = DEFAULT_HEADERS.select{|h, v| h != hs} + headers = DEFAULT_HEADERS.clone + headers.delete("Content-Type") res = put_body(path, body, headers) end diff --git a/devops-service/tests/generate_tests.rb b/devops-service/tests/generate_tests.rb index e5a0f4d..2d097d9 100755 --- a/devops-service/tests/generate_tests.rb +++ b/devops-service/tests/generate_tests.rb @@ -15,7 +15,7 @@ class Generator < OpenStruct config_file = ENV["DEVOPS_FEATURES_GENERATOR_CONFIG"] || ENV["CONFIG"] || CONFIG @config = YAML.load_file(File.new(config_file)) load_fixtures() - super(:config => @config) + super(:config => @config, :formatter => @formatter, :fixtures => @fixtures) end def configure! diff --git a/devops-service/tests/templates/api_v2/10_create/00_filter.feature.erb b/devops-service/tests/templates/api_v2/10_create/00_filter.feature.erb index 2a1198b..19ac70b 100644 --- a/devops-service/tests/templates/api_v2/10_create/00_filter.feature.erb +++ b/devops-service/tests/templates/api_v2/10_create/00_filter.feature.erb @@ -7,8 +7,8 @@ Feature: Filters Then response should be '401' @openstack - Scenario: Add openstack image filter without header 'Accept' - When I send PUT '/v2.0/filter/openstack/image' query with JSON body without header 'Accept' + Scenario: Add openstack image filter with header 'Accept' value 'text/*' + When I send PUT '/v2.0/filter/openstack/image' query with JSON body with header 'Accept' value 'text/*' """ [ "<%= @config["openstack"]["image"] %>" @@ -102,8 +102,8 @@ Feature: Filters Then response should be '401' @ec2 - Scenario: Add ec2 image filter without header 'Accept' - When I send PUT '/v2.0/filter/ec2/image' query with JSON body without header 'Accept' + Scenario: Add ec2 image filter with header 'Accept' value 'text/*' + When I send PUT '/v2.0/filter/ec2/image' query with JSON body with header 'Accept' value 'text/*' """ [ "<%= @config["ec2"]["image"] %>" diff --git a/devops-service/tests/templates/api_v2/90_delete/99_filter.feature.erb b/devops-service/tests/templates/api_v2/90_delete/99_filter.feature.erb index 4c10c7c..1fb2f54 100644 --- a/devops-service/tests/templates/api_v2/90_delete/99_filter.feature.erb +++ b/devops-service/tests/templates/api_v2/90_delete/99_filter.feature.erb @@ -7,8 +7,8 @@ Feature: Filters Then response should be '401' @openstack - Scenario: Delete openstack image filter without header 'Accept' - When I send DELETE '/v2.0/filter/openstack/image' query with JSON body without header 'Accept' + Scenario: Delete openstack image filter with header 'Accept' value 'text/*' + When I send DELETE '/v2.0/filter/openstack/image' query with JSON body with header 'Accept' value 'text/*' """ [ "<%= @config["openstack"]["image"] %>" @@ -92,8 +92,8 @@ Feature: Filters Then response should be '401' @ec2 - Scenario: Delete ec2 image filter without header 'Accept' - When I send DELETE '/v2.0/filter/ec2/image' query with JSON body without header 'Accept' + Scenario: Delete ec2 image filter with header 'Accept' value 'text/*' + When I send DELETE '/v2.0/filter/ec2/image' query with JSON body with header 'Accept' value 'text/*' """ [ "<%= @config["ec2"]["image"] %>"