executors: server executor
This commit is contained in:
		
							parent
							
								
									7a69d2b83c
								
							
						
					
					
						commit
						fb272395df
					
				| @ -1,4 +1,4 @@ | |||||||
| require "commands/deploy" | require "executors/server_executor" | ||||||
| require "commands/status" | require "commands/status" | ||||||
| require "workers/deploy_worker" | require "workers/deploy_worker" | ||||||
| require "exceptions/deploy_info_error" | require "exceptions/deploy_info_error" | ||||||
| @ -9,7 +9,7 @@ module Devops | |||||||
|   module API2_0 |   module API2_0 | ||||||
|     module Handler |     module Handler | ||||||
|       class Deploy < RequestHandler |       class Deploy < RequestHandler | ||||||
|         extend DeployCommands | #        extend DeployCommands | ||||||
|         extend StatusCommands |         extend StatusCommands | ||||||
| 
 | 
 | ||||||
|         set_parser Devops::API2_0::Parser::DeployParser |         set_parser Devops::API2_0::Parser::DeployParser | ||||||
| @ -81,7 +81,7 @@ module Devops | |||||||
|             end |             end | ||||||
|             begin |             begin | ||||||
|               deploy_info = create_deploy_info(s, project, body["build_number"]) |               deploy_info = create_deploy_info(s, project, body["build_number"]) | ||||||
|               res = deploy_server_proc.call(out, s, tags, deploy_info) |               res = Devops::Executor::ServerExecutor.new(s, out).deploy_server_with_tags(tags, deploy_info) | ||||||
|               status.push(res) |               status.push(res) | ||||||
|             rescue DeployInfoError => e |             rescue DeployInfoError => e | ||||||
|               msg = "Can not get deploy info: " + e.message |               msg = "Can not get deploy info: " + e.message | ||||||
|  | |||||||
| @ -4,7 +4,6 @@ require "uri" | |||||||
| require "commands/status" | require "commands/status" | ||||||
| require "commands/server" | require "commands/server" | ||||||
| require "commands/bootstrap_templates" | require "commands/bootstrap_templates" | ||||||
| require "commands/knife_commands" |  | ||||||
| 
 | 
 | ||||||
| require "providers/provider_factory" | require "providers/provider_factory" | ||||||
| 
 | 
 | ||||||
| @ -34,7 +33,7 @@ module Devops | |||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         def chef_servers |         def chef_servers | ||||||
|           KnifeCommands.chef_node_list |           KnifeFactory.instance.chef_node_list | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         def provider_servers provider |         def provider_servers provider | ||||||
| @ -148,8 +147,12 @@ module Devops | |||||||
|           cert = Devops::Db.connector.key s.key |           cert = Devops::Db.connector.key s.key | ||||||
|           DevopsLogger.logger.debug "Bootstrap certificate path: #{cert.path}" |           DevopsLogger.logger.debug "Bootstrap certificate path: #{cert.path}" | ||||||
|           #bootstrap s, out, cert.path, logger |           #bootstrap s, out, cert.path, logger | ||||||
|           provider = ::Provider::ProviderFactory.get(s.provider) |           options = { | ||||||
|           r = two_phase_bootstrap s, provider.run_list, bt, cert.path, out |             :bootstrap_template => bt, | ||||||
|  |             :cert_path => cert.path, | ||||||
|  |             :run_list => rl | ||||||
|  |           } | ||||||
|  |           r = two_phase_bootstrap s, options, out | ||||||
|           str = nil |           str = nil | ||||||
|           r = if check_server(s) |           r = if check_server(s) | ||||||
|             Devops::Db.connector.server_set_chef_node_name s |             Devops::Db.connector.server_set_chef_node_name s | ||||||
| @ -270,9 +273,9 @@ module Devops | |||||||
|           # server not found - OK |           # server not found - OK | ||||||
|           s = provider.servers.detect {|s| s["name"] == name} |           s = provider.servers.detect {|s| s["name"] == name} | ||||||
|           halt(400, "#{provider.name} node with name '#{name}' already exist") unless s.nil? |           halt(400, "#{provider.name} node with name '#{name}' already exist") unless s.nil? | ||||||
|           s = KnifeCommands.chef_node_list.detect {|n| n == name} |           s = KnifeFactory.instance.chef_node_list.detect {|n| n == name} | ||||||
|           halt(400, "Chef node with name '#{name}' already exist") unless s.nil? |           halt(400, "Chef node with name '#{name}' already exist") unless s.nil? | ||||||
|           s = KnifeCommands.chef_client_list.detect {|c| c == name} |           s = KnifeFactory.instance.chef_client_list.detect {|c| c == name} | ||||||
|           halt(400, "Chef client with name '#{name}' already exist") unless s.nil? |           halt(400, "Chef client with name '#{name}' already exist") unless s.nil? | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,37 +2,43 @@ require "json" | |||||||
| 
 | 
 | ||||||
| class KnifeCommands | class KnifeCommands | ||||||
| 
 | 
 | ||||||
|   def self.chef_node_list |   attr_accessor :config | ||||||
|  | 
 | ||||||
|  |   def initialize config | ||||||
|  |     self.config = config | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def chef_node_list | ||||||
|     knife("node list")[0].split.map{|c| c.strip} |     knife("node list")[0].split.map{|c| c.strip} | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.chef_client_list |   def chef_client_list | ||||||
|     knife("client list")[0].split.map{|c| c.strip} |     knife("client list")[0].split.map{|c| c.strip} | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.chef_node_delete name |   def chef_node_delete name | ||||||
|     o = knife("node delete #{name} -y")[0] |     o = knife("node delete #{name} -y")[0] | ||||||
|     (o.nil? ? o : o.strip) |     (o.nil? ? o : o.strip) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.chef_client_delete name |   def chef_client_delete name | ||||||
|     o = knife("client delete #{name} -y")[0] |     o = knife("client delete #{name} -y")[0] | ||||||
|     (o.nil? ? o : o.strip) |     (o.nil? ? o : o.strip) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.tags_list name |   def tags_list name | ||||||
|     knife("tag list #{name}")[0].split.map{|c| c.strip} |     knife("tag list #{name}")[0].split.map{|c| c.strip} | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.tags_create name, tagsStr |   def tags_create name, tagsStr | ||||||
|     knife("tag create #{name} #{tagsStr}") |     knife("tag create #{name} #{tagsStr}") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.tags_delete name, tagsStr |   def tags_delete name, tagsStr | ||||||
|     knife("tag delete #{name} #{tagsStr}") |     knife("tag delete #{name} #{tagsStr}") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.create_role role_name, project, env |   def create_role role_name, project, env | ||||||
|     file = "/tmp/new_role.json" |     file = "/tmp/new_role.json" | ||||||
|     File.open(file, "w") do |f| |     File.open(file, "w") do |f| | ||||||
|       f.puts <<-EOH |       f.puts <<-EOH | ||||||
| @ -51,31 +57,31 @@ class KnifeCommands | |||||||
| } | } | ||||||
| EOH | EOH | ||||||
|     end |     end | ||||||
|     out = `knife role from file #{file}` |     out, res = knife("role from file #{file}") | ||||||
|     raise "Cannot create role '#{role_name}': #{out}" unless $?.success? |     raise "Cannot create role '#{role_name}': #{out}" unless res | ||||||
|     true |     true | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.roles(chef_env=nil) |   def roles(chef_env=nil) | ||||||
|     o, s = knife("role list --format json") |     o, s = knife("role list --format json") | ||||||
|     return (s ? JSON.parse(o) : nil) |     return (s ? JSON.parse(o) : nil) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.role_name project_name, deploy_env |   def role_name project_name, deploy_env | ||||||
|     project_name + (DevopsConfig.config[:role_separator] || "_") + deploy_env |     project_name + (DevopsConfig.config[:role_separator] || "_") + deploy_env | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.knife cmd |   def knife cmd | ||||||
|     o = `knife #{cmd} 2>&1` |     o = `knife #{cmd} -c #{self.config} 2>&1` | ||||||
|     return o, $?.success? |     return o, $?.success? | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.ssh_options cmd, host, user, cert |   def ssh_options cmd, host, user, cert | ||||||
|     ["-m", "-x", user, "-i", cert, "--no-host-key-verify", host, "'#{(user == "root" ? cmd : "sudo #{cmd}")}'"] |     ["-m", "-x", user, "-i", cert, "--no-host-key-verify", host, "'#{(user == "root" ? cmd : "sudo #{cmd}")}'"] | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.ssh_stream out, cmd, host, user, cert |   def ssh_stream out, cmd, host, user, cert | ||||||
|     knife_cmd = "knife ssh -c #{get_config()} #{ssh_options(cmd, host, user, cert).join(" ")}" |     knife_cmd = "knife ssh -c #{self.config} #{ssh_options(cmd, host, user, cert).join(" ")}" | ||||||
|     out << "\nExecuting '#{knife_cmd}' \n\n" |     out << "\nExecuting '#{knife_cmd}' \n\n" | ||||||
|     out.flush if out.respond_to?(:flush) |     out.flush if out.respond_to?(:flush) | ||||||
|     status = 2 |     status = 2 | ||||||
| @ -91,13 +97,12 @@ EOH | |||||||
|     return lline |     return lline | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.knife_bootstrap out, ip, options |   def knife_bootstrap out, ip, options | ||||||
|     options << "-c ~/.chef/knife.rb" |  | ||||||
|     knife_stream(out, "bootstrap", options + [ ip ]) |     knife_stream(out, "bootstrap", options + [ ip ]) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.knife_stream out, cmd, options=[] |   def knife_stream out, cmd, options=[] | ||||||
|     knife_cmd = "knife #{cmd} #{options.join(" ")}" |     knife_cmd = "knife #{cmd} #{options.join(" ")} -c #{self.config}" | ||||||
|     out << "\nExecuting '#{knife_cmd}' \n\n" |     out << "\nExecuting '#{knife_cmd}' \n\n" | ||||||
|     out.flush if out.respond_to?(:flush) |     out.flush if out.respond_to?(:flush) | ||||||
|     status = nil |     status = nil | ||||||
| @ -112,7 +117,7 @@ EOH | |||||||
|     return status |     return status | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.set_run_list node, list |   def set_run_list node, list | ||||||
|     knife("node run_list set #{node} '#{list.join("','")}'") |     knife("node run_list set #{node} '#{list.join("','")}'") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,30 +6,6 @@ require "exceptions/record_not_found" | |||||||
| module ServerCommands | module ServerCommands | ||||||
| 
 | 
 | ||||||
|   include DeployCommands |   include DeployCommands | ||||||
| =begin |  | ||||||
|   def create_server_proc |  | ||||||
|     lambda do |out, s, provider| |  | ||||||
|       mongo = ::Devops::Db.connector |  | ||||||
|       begin |  | ||||||
|         out << "Create server...\n" |  | ||||||
|         out.flush if out.respond_to?(:flush) |  | ||||||
|         unless provider.create_server(s, out) |  | ||||||
|           return 3 |  | ||||||
|         end |  | ||||||
|         s.create |  | ||||||
|         out.flush if out.respond_to?(:flush) |  | ||||||
|         DevopsLogger.logger.info "Server with parameters: #{s.to_hash.inspect} is running" |  | ||||||
|         key = mongo.key(s.key) |  | ||||||
|         return two_phase_bootstrap(s, out, provider, key.path) |  | ||||||
|       rescue => e |  | ||||||
|         DevopsLogger.logger.error e.message |  | ||||||
|         DevopsLogger.logger.warn roll_back(s, provider) |  | ||||||
|         mongo.server_delete s.id |  | ||||||
|         return 5 |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| =end |  | ||||||
| 
 | 
 | ||||||
|   def create_server project, env, params, user, out |   def create_server project, env, params, user, out | ||||||
|     provider = ::Provider::ProviderFactory.get(env.provider) |     provider = ::Provider::ProviderFactory.get(env.provider) | ||||||
| @ -57,7 +33,11 @@ module ServerCommands | |||||||
|         s.run_list = Set.new.merge(project.run_list).merge(env.run_list).merge(s.run_list) |         s.run_list = Set.new.merge(project.run_list).merge(env.run_list).merge(s.run_list) | ||||||
|         key = mongo.key(s.key) |         key = mongo.key(s.key) | ||||||
|         s.chef_node_name = provider.create_default_chef_node_name(s) if s.chef_node_name.nil? |         s.chef_node_name = provider.create_default_chef_node_name(s) if s.chef_node_name.nil? | ||||||
|         return two_phase_bootstrap(s, provider.run_list, i.bootstrap_template, key.path, out) |         options = { | ||||||
|  |           bootstrap_template: i.bootstrap_template, | ||||||
|  |           cert_path: key.path | ||||||
|  |         } | ||||||
|  |         return two_phase_bootstrap(s, options, out) | ||||||
|       else |       else | ||||||
|         return 0 |         return 0 | ||||||
|       end |       end | ||||||
| @ -69,12 +49,13 @@ module ServerCommands | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def two_phase_bootstrap s, provider_run_list, bootstrap_template, cert_path, out |   def two_phase_bootstrap s, options, out | ||||||
|     provider = ::Provider::ProviderFactory.get(s.provider) |     provider = ::Provider::ProviderFactory.get(s.provider) | ||||||
|     mongo = ::Devops::Db.connector |     mongo = ::Devops::Db.connector | ||||||
|     out << "\n\nBootstrap...\n" |     out << "\n\nBootstrap...\n" | ||||||
|     out.flush if out.respond_to?(:flush) |     out.flush if out.respond_to?(:flush) | ||||||
|     status = bootstrap(s, out, bootstrap_template, provider_run_list, cert_path) |     options[:run_list] = provider.run_list | ||||||
|  |     status = bootstrap(s, options, out) | ||||||
|     out.flush if out.respond_to?(:flush) |     out.flush if out.respond_to?(:flush) | ||||||
|     if status == 0 |     if status == 0 | ||||||
|       DevopsLogger.logger.info "Server with id '#{s.id}' is bootstraped" |       DevopsLogger.logger.info "Server with id '#{s.id}' is bootstraped" | ||||||
| @ -88,7 +69,7 @@ module ServerCommands | |||||||
|       out << "\n" |       out << "\n" | ||||||
|       out.flush if out.respond_to?(:flush) |       out.flush if out.respond_to?(:flush) | ||||||
| 
 | 
 | ||||||
|       run_list = s.run_list + provider_run_list |       run_list = s.run_list + provider.run_list | ||||||
|       out << "\nRun list: #{run_list.inspect}" |       out << "\nRun list: #{run_list.inspect}" | ||||||
| #      s.options[:run_list] += run_list | #      s.options[:run_list] += run_list | ||||||
|       KnifeCommands.set_run_list(s.chef_node_name, run_list) |       KnifeCommands.set_run_list(s.chef_node_name, run_list) | ||||||
| @ -108,139 +89,6 @@ module ServerCommands | |||||||
|     return status |     return status | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def delete_from_chef_server node_name |  | ||||||
|     { |  | ||||||
|       :chef_node => KnifeCommands.chef_node_delete(node_name), |  | ||||||
|       :chef_client => KnifeCommands.chef_client_delete(node_name) |  | ||||||
|     } |  | ||||||
|   end |  | ||||||
| 
 | 
 | ||||||
|   def self.delete_etc_chef s, cert_path |  | ||||||
|     cmd = "ssh -i #{cert_path} -t -q #{s.remote_user}@#{s.private_ip}" |  | ||||||
|     cmd += " sudo " unless s.remote_user == "root" |  | ||||||
|     cmd += "rm -Rf /etc/chef" |  | ||||||
|     r = `#{cmd}` |  | ||||||
|     raise(r) unless $?.success? |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def check_server s |  | ||||||
|     KnifeCommands.chef_node_list.include?(s.chef_node_name) and KnifeCommands.chef_client_list.include?(s.chef_node_name) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def bootstrap s, out, bootstrap_template, run_list, cert_path |  | ||||||
|     out << "Before bootstrap hooks...\n" |  | ||||||
|     res = s.run_hook(:before_bootstrap, out) |  | ||||||
|     out << "Done\n" |  | ||||||
|     if s.private_ip.nil? |  | ||||||
|       out << "Error: Private IP is null" |  | ||||||
|       return false |  | ||||||
|     end |  | ||||||
|     out << "\nBootstrap with run list: #{run_list.inspect}\n" |  | ||||||
|     ja = { |  | ||||||
|       :provider => s.provider, |  | ||||||
|       :devops_host => `hostname`.strip |  | ||||||
|     } |  | ||||||
|     bootstrap_options = [ |  | ||||||
|       "-x #{s.remote_user}", |  | ||||||
|       "-i #{cert_path}", |  | ||||||
|       "--json-attributes '#{ja.to_json}'", |  | ||||||
|       "-N #{s.chef_node_name}" |  | ||||||
|     ] |  | ||||||
|     bootstrap_options.push "--sudo" unless s.remote_user == "root" |  | ||||||
|     bootstrap_options.push "-d #{bootstrap_template}" if bootstrap_template |  | ||||||
|     bootstrap_options.push "-r #{run_list.join(",")}" unless run_list.empty? |  | ||||||
|     ip = s.private_ip |  | ||||||
|     unless s.public_ip.nil? || s.public_ip.strip.empty? |  | ||||||
|       ip = s.public_ip |  | ||||||
|       out << "\nPublic IP is present\n" |  | ||||||
|     end |  | ||||||
|     out << "\nWaiting for SSH..." |  | ||||||
|     out.flush if out.respond_to?(:flush) |  | ||||||
|     i = 0 |  | ||||||
|     cmd = "ssh -i #{cert_path} -q #{s.remote_user}@#{ip} 'exit' 2>&1" |  | ||||||
|     begin |  | ||||||
|       sleep(5) |  | ||||||
|       res = `#{cmd}` |  | ||||||
|       i += 1 |  | ||||||
|       if i == 120 |  | ||||||
|         out << "\nCan not connect to #{s.remote_user}@#{ip}" |  | ||||||
|         out << "\n" + res |  | ||||||
|         DevopsLogger.logger.error "Can not connect with command 'ssh -i #{cert_path} #{s.remote_user}@#{ip}':\n#{res}" |  | ||||||
|         return false |  | ||||||
|       end |  | ||||||
|       raise ArgumentError.new("Can not connect with command '#{cmd}' ") unless $?.success? |  | ||||||
|     rescue ArgumentError => e |  | ||||||
|       retry |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     r = KnifeCommands.knife_bootstrap(out, ip, bootstrap_options) |  | ||||||
|     if r == 0 |  | ||||||
|       out << "Chef node name: #{s.chef_node_name}\n" |  | ||||||
|       ::Devops::Db.connector.server_set_chef_node_name s |  | ||||||
|       out << "Chef node name has been updated\n" |  | ||||||
|       out << "After bootstrap hooks...\n" |  | ||||||
|       res = s.run_hook(:after_bootstrap, out) |  | ||||||
|       out << "Done\n" |  | ||||||
|     else |  | ||||||
|     end |  | ||||||
|     r |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def self.unbootstrap s, cert_path |  | ||||||
|     i = 0 |  | ||||||
|     begin |  | ||||||
|       r = `ssh -i #{cert_path} -q #{s.remote_user}@#{s.private_ip} rm -Rf /etc/chef` |  | ||||||
|       raise(r) unless $?.success? |  | ||||||
|     rescue => e |  | ||||||
|       DevopsLogger.logger.error "Unbootstrap error: " + e.message |  | ||||||
|       i += 1 |  | ||||||
|       sleep(1) |  | ||||||
|       retry unless i == 5 |  | ||||||
|       return e.message |  | ||||||
|     end |  | ||||||
|     nil |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def delete_server s |  | ||||||
|     mongo = ::Devops::Db.connector |  | ||||||
|     if s.static? |  | ||||||
|       if !s.chef_node_name.nil? |  | ||||||
|         cert = mongo.key s.key |  | ||||||
|         ServerCommands.unbootstrap(s, cert.path) |  | ||||||
|       end |  | ||||||
|       mongo.server_delete s.id |  | ||||||
|       msg = "Static server '#{s.id}' is removed" |  | ||||||
|       DevopsLogger.logger.info msg |  | ||||||
|       return msg, nil |  | ||||||
|     end |  | ||||||
|     r = delete_from_chef_server(s.chef_node_name) |  | ||||||
|     provider = ::Provider::ProviderFactory.get(s.provider) |  | ||||||
|     begin |  | ||||||
|       r[:server] = provider.delete_server s |  | ||||||
|     rescue Fog::Compute::OpenStack::NotFound, Fog::Compute::AWS::NotFound |  | ||||||
|       r[:server] = "Server with id '#{s.id}' not found in '#{provider.name}' servers" |  | ||||||
|       DevopsLogger.logger.warn r[:server] |  | ||||||
|     end |  | ||||||
|     mongo.server_delete s.id |  | ||||||
|     info = "Server '#{s.id}' with name '#{s.chef_node_name}' for project '#{s.project}-#{s.deploy_env}' is removed" |  | ||||||
|     DevopsLogger.logger.info info |  | ||||||
|     r.each{|key, log| DevopsLogger.logger.info("#{key} - #{log}")} |  | ||||||
|     return info, r |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def roll_back s, provider |  | ||||||
|     str = "" |  | ||||||
|     unless s.id.nil? |  | ||||||
|       str << "Server '#{s.chef_node_name}' with id '#{s.id}' is not created\n" |  | ||||||
|       str << delete_from_chef_server(s.chef_node_name).values.join("\n") |  | ||||||
|       begin |  | ||||||
|         str << provider.delete_server(s) |  | ||||||
|       rescue => e |  | ||||||
|         str << e.message |  | ||||||
|       end |  | ||||||
|       str << "\nRolled back\n" |  | ||||||
|     end |  | ||||||
|     return str |  | ||||||
|   end |  | ||||||
| 
 | 
 | ||||||
| end | end | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ require_relative "devops-db" | |||||||
| require_relative "devops-logger" | require_relative "devops-logger" | ||||||
| require_relative "devops-application" | require_relative "devops-application" | ||||||
| 
 | 
 | ||||||
|  | require "knife/knife_factory" | ||||||
|  | 
 | ||||||
| require_relative "../sinatra/methods_with_headers" | require_relative "../sinatra/methods_with_headers" | ||||||
| 
 | 
 | ||||||
| require_relative "../applications" | require_relative "../applications" | ||||||
| @ -46,6 +48,7 @@ class DevopsService | |||||||
|     # 6. init all routes classes |     # 6. init all routes classes | ||||||
|     # 7. register routes for all classes |     # 7. register routes for all classes | ||||||
|     def init |     def init | ||||||
|  |       KnifeFactory.init | ||||||
|       # init database |       # init database | ||||||
|       Devops::Db.init |       Devops::Db.init | ||||||
|       DevopsLogger.logger = DevopsLogger.create(STDOUT) |       DevopsLogger.logger = DevopsLogger.create(STDOUT) | ||||||
|  | |||||||
							
								
								
									
										281
									
								
								devops-service/lib/executors/server_executor.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								devops-service/lib/executors/server_executor.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,281 @@ | |||||||
|  | require "knife/knife_factory" | ||||||
|  | 
 | ||||||
|  | module Devops | ||||||
|  |   module Executor | ||||||
|  |     class ServerExecutor | ||||||
|  | 
 | ||||||
|  |       def initialize server, out | ||||||
|  |         @project = Devops::Db.connector.project(server.project) | ||||||
|  |         @deploy_env = @project.deploy_env(server.deploy_env) | ||||||
|  |         @knife_instance = KnifeFactory.instance | ||||||
|  |         @server = server | ||||||
|  |         @out = out | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def create_server options | ||||||
|  |         provider = @server.provider_instance | ||||||
|  |         mongo = ::Devops::Db.connector | ||||||
|  |         begin | ||||||
|  |           @out << "Create server...\n" | ||||||
|  |           @out.flush if @out.respond_to?(:flush) | ||||||
|  | 
 | ||||||
|  | =begin | ||||||
|  |           s = Devops::Model::Server.new | ||||||
|  |           s.provider = provider.name | ||||||
|  |           s.project = self.id | ||||||
|  |           s.deploy_env = env.identifier | ||||||
|  |           s.run_list = options["run_list"] || [] | ||||||
|  |           s.chef_node_name = options["name"] | ||||||
|  |           s.key = options["key"] || provider.ssh_key | ||||||
|  | 
 | ||||||
|  |           s.created_by = user | ||||||
|  | =end | ||||||
|  |           i = mongo.image(@deploy_env.image) | ||||||
|  |           @server.remote_user = i.remote_user | ||||||
|  |           res = {} | ||||||
|  |           res[:before] = self.run_hook :before_create | ||||||
|  |           return false unless provider.create_server(@server, @deploy_env.image, @deploy_env.flavor, @deploy_env.subnets, @deploy_env.groups, @out) | ||||||
|  |           mongo.server_insert @server | ||||||
|  |           res[:after] = self.run_hook :after_create | ||||||
|  |           res | ||||||
|  | 
 | ||||||
|  | #          return 3 unless @server.create(provider, env.image, env.flavor, env.subnets, env.groups, @out) | ||||||
|  |           @out.flush if @out.respond_to?(:flush) | ||||||
|  |           DevopsLogger.logger.info "Server with parameters: #{@server.to_hash.inspect} is running" | ||||||
|  |           unless options["without_bootstrap"] | ||||||
|  |             @server.run_list = Set.new.merge(@project.run_list).merge(@deploy_env.run_list).merge(@server.run_list) | ||||||
|  |             @server.chef_node_name = provider.create_default_chef_node_name(@server) if @server.chef_node_name.nil? | ||||||
|  |             bootstrap_options = { | ||||||
|  |               bootstrap_template: i.bootstrap_template | ||||||
|  |             } | ||||||
|  |             return bootstrap(bootstrap_options) | ||||||
|  |           else | ||||||
|  |             return 0 | ||||||
|  |           end | ||||||
|  |         rescue => e | ||||||
|  |           DevopsLogger.logger.error e.message | ||||||
|  |           roll_back | ||||||
|  |           mongo.server_delete @server.id | ||||||
|  |           return 5 | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def bootstrap options | ||||||
|  |         k = Devops::Db.connector.key(@server.key) | ||||||
|  |         cert_path = k.path | ||||||
|  |         @out << "Before bootstrap hooks...\n" | ||||||
|  |         res = self.run_hook(:before_bootstrap, @out) | ||||||
|  |         @out << "Done\n" | ||||||
|  |         if @server.private_ip.nil? | ||||||
|  |           @out << "Error: Private IP is null" | ||||||
|  |           return false | ||||||
|  |         end | ||||||
|  |         ja = { | ||||||
|  |           :provider => @server.provider, | ||||||
|  |           :devops_host => `hostname`.strip | ||||||
|  |         } | ||||||
|  |         ip = @server.private_ip | ||||||
|  |         unless @server.public_ip.nil? | ||||||
|  |           ip = @server.public_ip | ||||||
|  |           @out << "\nPublic IP is present\n" | ||||||
|  |         end | ||||||
|  |         @out << "\nWaiting for SSH..." | ||||||
|  |         @out.flush if @out.respond_to?(:flush) | ||||||
|  |         i = 0 | ||||||
|  |         cmd = "ssh -i #{cert_path} -q #{@server.remote_user}@#{ip} 'exit' 2>&1" | ||||||
|  |         begin | ||||||
|  |           sleep(5) | ||||||
|  |           res = `#{cmd}` | ||||||
|  |           i += 1 | ||||||
|  |           if i == 120 | ||||||
|  |             @out << "\nCan not connect to #{@server.remote_user}@#{ip}" | ||||||
|  |             @out << "\n" + res | ||||||
|  |             DevopsLogger.logger.error "Can not connect with command 'ssh -i #{cert_path} #{@server.remote_user}@#{ip}':\n#{res}" | ||||||
|  |             return false | ||||||
|  |           end | ||||||
|  |           raise ArgumentError.new("Can not connect with command '#{cmd}' ") unless $?.success? | ||||||
|  |         rescue ArgumentError => e | ||||||
|  |           retry | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         r = @knife_instance.knife_bootstrap(out, ip, self.bootstrap_options(ja, options)) | ||||||
|  |         if r == 0 | ||||||
|  |           @out << "Chef node name: #{@server.chef_node_name}\n" | ||||||
|  |           ::Devops::Db.connector.server_set_chef_node_name @server | ||||||
|  |           @out << "Chef node name has been updated\n" | ||||||
|  |           @out << "After bootstrap hooks...\n" | ||||||
|  |           res = self.run_hook(:after_bootstrap, @out) | ||||||
|  |           @out << "Done\n" | ||||||
|  |         else | ||||||
|  |         end | ||||||
|  |         r | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def bootstrap_options attributes, options | ||||||
|  |         bootstrap_options = [ | ||||||
|  |           "-x #{@server.remote_user}", | ||||||
|  |           "-i #{options[:cert_path]}", | ||||||
|  |           "--json-attributes '#{attributes.to_json}'", | ||||||
|  |           "-N #{@server.chef_node_name}" | ||||||
|  |         ] | ||||||
|  |         bootstrap_options.push "--sudo" unless @server.remote_user == "root" | ||||||
|  |         bootstrap_options.push "-t #{options[:bootstrap_template]}" if options[:bootstrap_template] | ||||||
|  |         rl = options[:run_list] | ||||||
|  |         bootstrap_options.push "-r #{rl.join(",")}" unless rl.nil?# rl.empty? | ||||||
|  |         bootstrap_options.push "-c #{options[:config]}" if options[:config] | ||||||
|  |         bootstrap_options | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def check_server | ||||||
|  |         @knife_instance.chef_node_list.include?(@server.chef_node_name) and @knife_instance.chef_client_list.include?(@server.chef_node_name) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def unbootstrap | ||||||
|  |         k = Devops::Db.connector.key(@server.key) | ||||||
|  |         cert_path = k.path | ||||||
|  |         i = 0 | ||||||
|  |         begin | ||||||
|  |           r = `ssh -i #{cert_path} -q #{@server.remote_user}@#{@server.private_ip} rm -Rf /etc/chef` | ||||||
|  |           raise(r) unless $?.success? | ||||||
|  |         rescue => e | ||||||
|  |           DevopsLogger.logger.error "Unbootstrap error: " + e.message | ||||||
|  |           i += 1 | ||||||
|  |           sleep(1) | ||||||
|  |           retry unless i == 5 | ||||||
|  |           return e.message | ||||||
|  |         end | ||||||
|  |         nil | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def deploy_server_with_tags tags, deploy_info | ||||||
|  |         old_tags_str = nil | ||||||
|  |         new_tags_str = nil | ||||||
|  |         unless tags.empty? | ||||||
|  |           old_tags_str = @knife_instance.tags_list(@server.chef_node_name).join(" ") | ||||||
|  |           @out << "Server tags: #{old_tags_str}\n" | ||||||
|  |           @knife_instance.tags_delete(@server.chef_node_name, old_tags_str) | ||||||
|  | 
 | ||||||
|  |           new_tags_str = tags.join(" ") | ||||||
|  |           @out << "Server new tags: #{new_tags_str}\n" | ||||||
|  |           cmd = @knife_instance.tags_create(@server.chef_node_name, new_tags_str) | ||||||
|  |           unless cmd[1] | ||||||
|  |             m = "Error: Cannot add tags '#{new_tags_str}' to server '#{@server.chef_node_name}'" | ||||||
|  |             DevopsLogger.logger.error(m) | ||||||
|  |             @out << m + "\n" | ||||||
|  |             return 3 | ||||||
|  |           end | ||||||
|  |           DevopsLogger.logger.info("Set tags for '#{@server.chef_node_name}': #{new_tags_str}") | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         deploy_server deploy_info | ||||||
|  | 
 | ||||||
|  |         unless tags.empty? | ||||||
|  |           @out << "Restore tags\n" | ||||||
|  |           cmd = @knife_instance.tags_delete(@server.chef_node_name, new_tags_str) | ||||||
|  |           DevopsLogger.logger.info("Deleted tags for #{@server.chef_node_name}: #{new_tags_str}") | ||||||
|  |           cmd = @knife_instance.tags_create(@server.chef_node_name, old_tags_str) | ||||||
|  |           DevopsLogger.logger.info("Set tags for #{@server.chef_node_name}: #{old_tags_str}") | ||||||
|  |         end | ||||||
|  |         return r | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def deploy_server deploy_info | ||||||
|  |         @out << "Before deploy hooks...\n" | ||||||
|  |         res = self.run_hook(:before_deploy, @out, deploy_info) | ||||||
|  |         @out << "Done\n" | ||||||
|  |         @out << "\nRun chef-client on '#{@server.chef_node_name}'\n" | ||||||
|  |         cmd = "chef-client --no-color" | ||||||
|  |         if deploy_info["use_json_file"] | ||||||
|  |           deploy_info.delete["use_json_file"] | ||||||
|  |           @out << "Build information:\n" | ||||||
|  |           json = JSON.pretty_generate(deploy_info) | ||||||
|  |           @out << json | ||||||
|  |           @out << "\n" | ||||||
|  |           file = "#{@server.project}_#{@server.deploy_env}_#{Time.new.to_i}" | ||||||
|  |           dir = DevopsConfig.config[:project_info_dir] | ||||||
|  |           File.open(File.join(dir, file), "w") do |f| | ||||||
|  |             f.write json | ||||||
|  |           end | ||||||
|  |           @out.flush if @out.respond_to?(:flush) | ||||||
|  |           cmd << " -j http://#{DevopsConfig.config[:address]}:#{DevopsConfig.config[:port]}/#{DevopsConfig.config[:url_prefix]}/v2.0/deploy/data/#{file}" | ||||||
|  |         end | ||||||
|  |         ip = if @server.public_ip.nil? | ||||||
|  |           @server.private_ip | ||||||
|  |         else | ||||||
|  |           @out << "Public IP detected\n" | ||||||
|  |           @server.public_ip | ||||||
|  |         end | ||||||
|  |         @out.flush if @out.respond_to?(:flush) | ||||||
|  |         k = Devops::Db.connector.key(@server.key) | ||||||
|  |         lline = @knife_instance.ssh_stream(@out, cmd, ip, @server.remote_user, k.path) | ||||||
|  |         r = /Chef\sClient\sfinished/i | ||||||
|  |         if lline[r].nil? | ||||||
|  |           1 | ||||||
|  |         else | ||||||
|  |           @out << "After deploy hooks...\n" | ||||||
|  |           res = server.run_hook(:after_deploy, @out, deploy_info) | ||||||
|  |           @out << "Done\n" | ||||||
|  |           0 | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def delete_from_chef_server node_name | ||||||
|  |         { | ||||||
|  |           :chef_node => @knife_instance.chef_node_delete(node_name), | ||||||
|  |           :chef_client => @knife_instance.chef_client_delete(node_name) | ||||||
|  |         } | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  | =begin | ||||||
|  |       def delete_etc_chef s, cert_path | ||||||
|  |         cmd = "ssh -i #{cert_path} -t -q #{s.remote_user}@#{s.private_ip}" | ||||||
|  |         cmd += " sudo " unless s.remote_user == "root" | ||||||
|  |         cmd += "rm -Rf /etc/chef" | ||||||
|  |         r = `#{cmd}` | ||||||
|  |         raise(r) unless $?.success? | ||||||
|  |       end | ||||||
|  | =end | ||||||
|  | 
 | ||||||
|  |       def delete_server | ||||||
|  |         mongo = ::Devops::Db.connector | ||||||
|  |         if @server.static? | ||||||
|  |           if !@server.chef_node_name.nil? | ||||||
|  |             unbootstrap | ||||||
|  |           end | ||||||
|  |           mongo.server_delete @server.id | ||||||
|  |           msg = "Static server '#{@server.id}' is removed" | ||||||
|  |           DevopsLogger.logger.info msg | ||||||
|  |           return msg, nil | ||||||
|  |         end | ||||||
|  |         r = delete_from_chef_server(@server.chef_node_name) | ||||||
|  |         provider = @server.provider_instance | ||||||
|  |         begin | ||||||
|  |           r[:server] = provider.delete_server @server | ||||||
|  |         rescue Fog::Compute::OpenStack::NotFound, Fog::Compute::AWS::NotFound | ||||||
|  |           r[:server] = "Server with id '#{@server.id}' not found in '#{provider.name}' servers" | ||||||
|  |           DevopsLogger.logger.warn r[:server] | ||||||
|  |         end | ||||||
|  |         mongo.server_delete @server.id | ||||||
|  |         info = "Server '#{@server.id}' with name '#{@server.chef_node_name}' for project '#{@server.project}-#{@server.deploy_env}' is removed" | ||||||
|  |         DevopsLogger.logger.info info | ||||||
|  |         r.each{|key, log| DevopsLogger.logger.info("#{key} - #{log}")} | ||||||
|  |         return info, r | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def roll_back | ||||||
|  |         unless @server.id.nil? | ||||||
|  |           @out << "Server '#{@server.chef_node_name}' with id '#{@server.id}' is not created\n" | ||||||
|  |           @out << delete_from_chef_server(@server.chef_node_name).values.join("\n") | ||||||
|  |           begin | ||||||
|  |             @out << @server.provider_instance.delete_server(@server) | ||||||
|  |           rescue => e | ||||||
|  |             @out << e.message | ||||||
|  |           end | ||||||
|  |           @out << "\nRolled back\n" | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										17
									
								
								devops-service/lib/knife/knife_factory.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								devops-service/lib/knife/knife_factory.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | require "commands/knife_commands" | ||||||
|  | 
 | ||||||
|  | class KnifeFactory | ||||||
|  | 
 | ||||||
|  |   class << self | ||||||
|  |     def init | ||||||
|  |       c = DevopsConfig.config[:knife_config] | ||||||
|  |       raise "Option ':knife_config' is undefined, please check config.rb file" unless c | ||||||
|  |       @instance = KnifeCommands.new(c) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     def instance | ||||||
|  |       @instance | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  | end | ||||||
| @ -1,11 +1,10 @@ | |||||||
| require File.join(File.dirname(__FILE__), "worker") | require File.join(File.dirname(__FILE__), "worker") | ||||||
| 
 | 
 | ||||||
| require "commands/deploy" | require "lib/executors/server_executor" | ||||||
| require "db/mongo/models/server" | require "db/mongo/models/server" | ||||||
| require "db/mongo/models/report" | require "db/mongo/models/report" | ||||||
| 
 | 
 | ||||||
| class DeployWorker < Worker | class DeployWorker < Worker | ||||||
|   include DeployCommands |  | ||||||
| 
 | 
 | ||||||
|   def perform(dir, server, tags, owner, conf, deploy_info) |   def perform(dir, server, tags, owner, conf, deploy_info) | ||||||
|     call(conf, nil, dir) do |provider, out, file| |     call(conf, nil, dir) do |provider, out, file| | ||||||
| @ -24,7 +23,7 @@ class DeployWorker < Worker | |||||||
|       } |       } | ||||||
|       mongo.save_report(Report.new(o)) |       mongo.save_report(Report.new(o)) | ||||||
| 
 | 
 | ||||||
|       status = deploy_server_proc.call(out, s, tags, deploy_info) |       status = Devops::Executor::ServerExecutor.new(s, out).deploy_server_with_tags(tags, deploy_info) | ||||||
|       status |       status | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Anton Martynov
						Anton Martynov