182 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| require "providers/provider_factory"
 | |
| require "routes/v2.0/base_routes"
 | |
| require "fileutils"
 | |
| require "commands/status"
 | |
| 
 | |
| module Version2_0
 | |
|   class ScriptRoutes < BaseRoutes
 | |
| 
 | |
|     include StatusCommands
 | |
| 
 | |
|     def initialize wrapper
 | |
|       super wrapper
 | |
|       puts "Script routes initialized"
 | |
|     end
 | |
| 
 | |
|     before "/script/:script_name" do
 | |
|       check_headers :accept
 | |
|       check_privileges("script", "w")
 | |
|       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"))
 | |
|       if request.put?
 | |
|         halt_response("File '#{file_name}' already exist") if File.exists?(@file)
 | |
|       elsif request.delete?
 | |
|         halt_response("File '#{file_name}' does not exist", 404) unless File.exists?(@file)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     after %r{\A/script/((command|run)/)?[\w]+\z} do
 | |
|       statistic
 | |
|     end
 | |
| 
 | |
|     # Get scripts names
 | |
|     #
 | |
|     # * *Request*
 | |
|     #   - method : GET
 | |
|     #   - headers :
 | |
|     #     - Accept: application/json
 | |
|     #
 | |
|     # * *Returns* :
 | |
|     #   [
 | |
|     #     "script_1"
 | |
|     #   ]
 | |
|     get "/scripts" do
 | |
|       check_headers :accept
 | |
|       check_privileges("script", "r")
 | |
|       res = []
 | |
|       Dir.foreach(DevopsService.config[:scripts_dir]) {|f| res.push(f) unless f.start_with?(".")}
 | |
|       json res
 | |
|     end
 | |
| 
 | |
|     # Run command on node :node_name
 | |
|     #
 | |
|     # * *Request*
 | |
|     #   - method : POST
 | |
|     #   - body :
 | |
|     #     command to run
 | |
|     #
 | |
|     # * *Returns* : text stream
 | |
|     post "/script/command/:node_name" do
 | |
|       check_privileges("script", "x")
 | |
|       user = request.env['REMOTE_USER']
 | |
|       s = BaseRoutes.mongo.server_by_chef_node_name params[:node_name]
 | |
|       BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, user
 | |
|       cert = BaseRoutes.mongo.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
 | |
| 
 | |
|     # 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
 | |
|     post "/script/run/:script_name" do
 | |
|       check_headers :content_type
 | |
|       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 = BaseRoutes.mongo.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|
 | |
|         BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, user
 | |
|       end
 | |
|       stream() do |out|
 | |
|         begin
 | |
|           status = []
 | |
|           servers.each do |s|
 | |
|             cert = begin
 | |
|               BaseRoutes.mongo.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
 | |
| 
 | |
|     # Create script :script_name
 | |
|     #
 | |
|     # * *Request*
 | |
|     #   - method : PUT
 | |
|     #   - headers :
 | |
|     #     - Accept: application/json
 | |
|     #   - body : script content
 | |
|     #
 | |
|     # * *Returns* :
 | |
|     #   201 - Created
 | |
|     put "/script/:script_name" do
 | |
|       File.open(@file, "w") {|f| f.write(request.body.read)}
 | |
|       create_response("File '#{params[:script_name]}' created", nil, 201)
 | |
|     end
 | |
| 
 | |
|     # Delete script :script_name
 | |
|     #
 | |
|     # * *Request*
 | |
|     #   - method : Delete
 | |
|     #   - headers :
 | |
|     #     - Accept: application/json
 | |
|     #
 | |
|     # * *Returns* :
 | |
|     #   200 - Deleted
 | |
|     delete "/script/:script_name" do
 | |
|       FileUtils.rm(@file)
 | |
|       create_response("File '#{params[:script_name]}' deleted")
 | |
|     end
 | |
|   end
 | |
| end
 | 
