diff --git a/devops-client/lib/devops-client/handler/server.rb b/devops-client/lib/devops-client/handler/server.rb index 1fb6e89..37cf7e0 100644 --- a/devops-client/lib/devops-client/handler/server.rb +++ b/devops-client/lib/devops-client/handler/server.rb @@ -40,6 +40,12 @@ class Server < Handler when "unpause" self.options = @options_parser.unpause_options unpause_handler @options_parser.args + when "reserve" + self.options = @options_parser.reserve_options + reserve_handler @options_parser.args + when "unreserve" + self.options = @options_parser.unreserve_options + unreserve_handler @options_parser.args when "add" self.options = @options_parser.add_options add_static_handler @options_parser.args @@ -152,7 +158,7 @@ class Server < Handler @options_parser.invalid_pause_command abort(r) end - post "/server/#{args[2]}/pause" + post "/server/#{args[2]}/pause", options end def unpause_handler args @@ -161,7 +167,25 @@ class Server < Handler @options_parser.invalid_unpause_command abort(r) end - post "/server/#{args[2]}/unpause" + post "/server/#{args[2]}/unpause", options + end + + def reserve_handler args + r = inspect_parameters @options_parser.reserve_params, args[2] + unless r.nil? + @options_parser.invalid_reserve_command + abort(r) + end + post "/server/#{args[2]}/reserve", options + end + + def unreserve_handler args + r = inspect_parameters @options_parser.unreserve_params, args[2] + unless r.nil? + @options_parser.invalid_unreserve_command + abort(r) + end + post "/server/#{args[2]}/unreserve", options end end diff --git a/devops-client/lib/devops-client/options/server_options.rb b/devops-client/lib/devops-client/options/server_options.rb index ee6ce09..b18aee8 100644 --- a/devops-client/lib/devops-client/options/server_options.rb +++ b/devops-client/lib/devops-client/options/server_options.rb @@ -2,7 +2,7 @@ require "devops-client/options/common_options" class ServerOptions < CommonOptions - commands :add, :bootstrap, :create, :delete, :list, :pause, :show, :unpause # :sync, + commands :add, :bootstrap, :create, :delete, :list, :pause, :reserve, :show, :unpause, :unreserve # :sync, def initialize args, def_options super(args, def_options) @@ -15,6 +15,8 @@ class ServerOptions < CommonOptions self.show_params = node_params self.pause_params = node_params self.unpause_params = node_params + self.reserve_params = node_params + self.unreserve_params = node_params self.bootstrap_params = ["INSTANCE_ID"] self.add_params = ["PROJECT_ID", "DEPLOY_ENV", "IP", "SSH_USER", "KEY_ID"] end @@ -34,6 +36,46 @@ class ServerOptions < CommonOptions end end + def pause_options + options do |opts, options| + opts.banner << self.delete_banner + options[:key] = "node" + opts.on('--instance', "Pause server by instance id") do + options[:key] = "instance" + end + end + end + + def unpause_options + options do |opts, options| + opts.banner << self.delete_banner + options[:key] = "node" + opts.on('--instance', "Unpause server by instance id") do + options[:key] = "instance" + end + end + end + + def reserve_options + options do |opts, options| + opts.banner << self.delete_banner + options[:key] = "node" + opts.on('--instance', "Reserve server by instance id") do + options[:key] = "instance" + end + end + end + + def unreserve_options + options do |opts, options| + opts.banner << self.delete_banner + options[:key] = "node" + opts.on('--instance', "Unreserve server by instance id") do + options[:key] = "instance" + end + end + end + def create_options options do |opts, options| opts.banner << self.create_banner diff --git a/devops-client/lib/devops-client/version.rb b/devops-client/lib/devops-client/version.rb index d01f705..ab0ef1e 100644 --- a/devops-client/lib/devops-client/version.rb +++ b/devops-client/lib/devops-client/version.rb @@ -1,3 +1,3 @@ module DevopsClient - VERSION = "2.1.29" + VERSION = "2.1.30" end diff --git a/devops-service/db/mongo/models/server.rb b/devops-service/db/mongo/models/server.rb index a8ca217..ad55a4d 100644 --- a/devops-service/db/mongo/models/server.rb +++ b/devops-service/db/mongo/models/server.rb @@ -3,7 +3,7 @@ require "db/mongo/models/mongo_model" class Server < MongoModel - attr_accessor :provider, :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by + attr_accessor :provider, :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by, :reserved_by attr_accessor :options, :static, :key types :id => {:type => String, :empty => false}, @@ -15,7 +15,8 @@ class Server < MongoModel :public_ip => {:type => String, :empty => true, :nil => true}, :key => {:type => String, :empty => false}, :created_by => {:type => String, :empty => false}, - :chef_node_name => {:type => String, :empty => true} + :chef_node_name => {:type => String, :empty => true}, + :reserved_by => {:type => String, :empty => true} def initialize self.static = false @@ -38,7 +39,8 @@ class Server < MongoModel "created_at" => self.created_at, "created_by" => self.created_by, "static" => self.static, - "key" => self.key + "key" => self.key, + "reserved_by" => self.reserved_by } end @@ -63,6 +65,7 @@ class Server < MongoModel server.created_by = s["created_by"] server.static = s["static"] || false server.key = s["key"] + server.reserved_by = s["reserved_by"] server end diff --git a/devops-service/routes/v2.0/deploy.rb b/devops-service/routes/v2.0/deploy.rb index b9ea528..30dd8b4 100644 --- a/devops-service/routes/v2.0/deploy.rb +++ b/devops-service/routes/v2.0/deploy.rb @@ -19,7 +19,7 @@ module Version2_0 statistic end - # Run chef-client on some instances + # Run chef-client on reserved server # # * *Request* # - method : POST @@ -40,7 +40,8 @@ module Version2_0 tags = check_array(r["tags"], "Parameter 'tags' should be an array of strings", String, true) || [] servers = BaseRoutes.mongo.servers_by_names(names) - halt(404, "No servers found for names '#{names.join("', '")}'") if servers.empty? + servers.delete_if{|s| s.reserved_by.nil?} + halt(404, "No reserved servers found for names '#{names.join("', '")}'") if servers.empty? keys = {} servers.sort_by!{|s| names.index(s.chef_node_name)} stream() do |out| diff --git a/devops-service/routes/v2.0/project.rb b/devops-service/routes/v2.0/project.rb index cfdec35..946c524 100644 --- a/devops-service/routes/v2.0/project.rb +++ b/devops-service/routes/v2.0/project.rb @@ -349,7 +349,7 @@ module Version2_0 create_response(info) end - # Run chef-client on project servers + # Run chef-client on reserved project servers # # * *Request* # - method : POST @@ -372,6 +372,7 @@ module Version2_0 check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true) project = BaseRoutes.mongo.project(params[:id]) servers = BaseRoutes.mongo.servers(params[:id], obj["deploy_env"]) + servers.delete_if{|s| s.reserved_by.nil?} unless obj["servers"].nil? logger.debug "Servers in params: #{obj["servers"].inspect}\nServers: #{servers.map{|s| s.chef_node_name}.inspect}" servers.select!{|ps| obj["servers"].include?(ps.chef_node_name)} @@ -379,7 +380,7 @@ module Version2_0 keys = {} stream() do |out| begin - out << (servers.empty? ? "No servers to deploy\n" : "Deploy servers: '#{servers.map{|s| s.chef_node_name}.join("', '")}'\n") + 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| diff --git a/devops-service/routes/v2.0/server.rb b/devops-service/routes/v2.0/server.rb index 3414bad..4e2531e 100644 --- a/devops-service/routes/v2.0/server.rb +++ b/devops-service/routes/v2.0/server.rb @@ -47,9 +47,11 @@ module Version2_0 check_privileges("server") end - before %r{\A/server/[\w]+/(un)?pause\z} do + before %r{\A/server/[\w]+/(pause|unpouse|reserve|unreserve)\z} do check_headers :accept, :content_type check_privileges("server", "w") + body = create_object_from_json_body(Hash, true) + @key = (body.nil? ? nil : body["key"]) end before "/servers/:provider" do @@ -146,6 +148,8 @@ module Version2_0 # - method : GET # - headers : # - Accept: application/json + # - parameters: + # key=instance -> search server by instance_id rather then chef_node_name # # * *Returns* : # [ @@ -154,7 +158,7 @@ module Version2_0 # } # ] get "/server/:name" do - json BaseRoutes.mongo.server_by_chef_node_name(params[:name]) + json get_server(params[:name], params[:key]).to_hash end # Delete devops server @@ -164,18 +168,17 @@ module Version2_0 # - headers : # - Accept: application/json # - Content-Type: application/json - # - body - # { - # "key": "instance" -> if key=instance, then :name - instance id, if key=null, then name - node name - # } + # - body : + # { + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } # # * *Returns* : # 200 - Deleted delete "/server/:id" do body = create_object_from_json_body(Hash, true) key = (body.nil? ? nil : body["key"]) - id = params[:id] - s = (key == "instance" ? BaseRoutes.mongo.server_by_instance_id(id) : BaseRoutes.mongo.server_by_chef_node_name(id)) + s = get_server(params[:id], key) ### Authorization BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] info, r = delete_server(s, BaseRoutes.mongo, logger) @@ -271,11 +274,16 @@ module Version2_0 # - method : POST # - headers : # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } # # * *Returns* : # 200 - Paused post "/server/:node_name/pause" do - s = BaseRoutes.mongo.server_by_chef_node_name params[:node_name] + s = get_server(params[:node_name], @key) ## Authorization BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] provider = ::Version2_0::Provider::ProviderFactory.get(s.provider) @@ -293,11 +301,16 @@ module Version2_0 # - method : POST # - headers : # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } # # * *Returns* : # 200 - Unpaused post "/server/:node_name/unpause" do - s = BaseRoutes.mongo.server_by_chef_node_name params[:node_name] + s = get_server(params[:node_name], @key) ## Authorization BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER'] provider = ::Version2_0::Provider::ProviderFactory.get(s.provider) @@ -309,6 +322,53 @@ module Version2_0 end end + # Reserve devops server + # + # * *Request* + # - method : POST + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } + # + # * *Returns* : + # 200 - Reserved + post "/server/:node_name/reserve" do + s = get_server(params[:node_name], params[:key]) + user = request.env['REMOTE_USER'] + BaseRoutes.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 + BaseRoutes.mongo.server_update(s) + create_response("Server '#{params[:node_name]}' has been reserved") + end + + # Unreserve devops server + # + # * *Request* + # - method : POST + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "key": "instance", -> search server by instance_id rather then chef_node_name + # } + # + # * *Returns* : + # 200 - Unreserved + post "/server/:node_name/unreserve" do + s = get_server(params[:node_name], params[:key]) + BaseRoutes.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 + BaseRoutes.mongo.server_update(s) + create_response("Server '#{params[:node_name]}' has been unreserved") + end + # Bootstrap devops server # # * *Request* @@ -416,6 +476,10 @@ module Version2_0 end private + def get_server id, key + key == "instance" ? BaseRoutes.mongo.server_by_instance_id(id) : BaseRoutes.mongo.server_by_chef_node_name(id) + end + def roll_back s, provider str = "" unless s.id.nil?