From 0b3b10e1dfc7a97c69d96b5d2c03397d57264dad Mon Sep 17 00:00:00 2001 From: Anton Martynov Date: Wed, 11 Nov 2015 14:35:27 +0300 Subject: [PATCH 01/18] #840: remove json_file from deploy_info --- devops-service/lib/executors/server_executor.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devops-service/lib/executors/server_executor.rb b/devops-service/lib/executors/server_executor.rb index 1d57df3..9532cf4 100644 --- a/devops-service/lib/executors/server_executor.rb +++ b/devops-service/lib/executors/server_executor.rb @@ -360,7 +360,7 @@ module Devops deploy_info.delete("use_json_file") json = nil dir = DevopsConfig.config[:project_info_dir] - file = deploy_info["json_file"] || "#{@server.project}_#{@server.deploy_env}_#{Time.new.to_i}" + file = deploy_info.delete("json_file") || "#{@server.project}_#{@server.deploy_env}_#{Time.new.to_i}" path = File.join(dir, file) if File.exists?(path) json = File.read(path) From df38e8611702975a1eb6e282848d9ee284f66c5a Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 13 Nov 2015 17:22:55 +0300 Subject: [PATCH 02/18] #871: ability to delete all project servers --- .../handler/helpers/outputtable.rb | 9 +++--- .../lib/devops-client/handler/project.rb | 18 +++++++++++ .../devops-client/options/project_options.rb | 10 ++++-- .../lib/devops-client/output/project.rb | 28 +++++++++++++++++ devops-client/locales/en.yml | 2 ++ devops-service/app/api2/handlers/project.rb | 28 +++++++++++++++++ devops-service/app/api2/helpers/parser.rb | 10 +++++- devops-service/app/api2/parsers/project.rb | 7 +++++ devops-service/app/api2/routes/project.rb | 31 +++++++++++++++++-- .../lib/executors/server_executor.rb | 4 ++- devops-service/providers/ec2.rb | 3 +- 11 files changed, 138 insertions(+), 12 deletions(-) diff --git a/devops-client/lib/devops-client/handler/helpers/outputtable.rb b/devops-client/lib/devops-client/handler/helpers/outputtable.rb index e7b948c..1f78db3 100644 --- a/devops-client/lib/devops-client/handler/helpers/outputtable.rb +++ b/devops-client/lib/devops-client/handler/helpers/outputtable.rb @@ -5,8 +5,8 @@ module Outputtable end def outputter - raise 'You should use "output_with" method to define outputter' unless defined?(outputter_class) - @outputter ||= outputter_class.new(data_to_output, options.merge(current_command: current_command)) + raise 'You should use "output_with" method to define outputter' unless self.class.outputter_class + @outputter ||= self.class.outputter_class.new(data_to_output, options.merge(current_command: current_command)) end def output(options={}) @@ -19,10 +19,9 @@ module Outputtable end module ClassMethods + attr_reader :outputter_class def output_with(klass) - define_method :outputter_class do - klass - end + @outputter_class = klass end end diff --git a/devops-client/lib/devops-client/handler/project.rb b/devops-client/lib/devops-client/handler/project.rb index 80feb88..bf6b7aa 100644 --- a/devops-client/lib/devops-client/handler/project.rb +++ b/devops-client/lib/devops-client/handler/project.rb @@ -84,6 +84,10 @@ class Project < Handler when "test" self.options = @options_parser.test_options test_handler @options_parser.args + when "delete_servers" + self.options = @options_parser.delete_servers_options + delete_servers_handler @options_parser.args + output(output_type: :delete_servers) else @options_parser.invalid_command end @@ -440,4 +444,18 @@ protected @list || @show || @servers || @test end + def delete_servers_handler(args) + project, env = args[2], args[3] + if error = inspect_parameters(@options_parser.delete_servers_params, project, env) + @options_parser.invalid_delete_servers_command + abort(error) + end + + body = { + deploy_env: env, + dry_run: @options[:dry_run] + } + @list = delete("/project/#{project}/servers", body) + end + end diff --git a/devops-client/lib/devops-client/options/project_options.rb b/devops-client/lib/devops-client/options/project_options.rb index 96ad738..3bbd72e 100644 --- a/devops-client/lib/devops-client/options/project_options.rb +++ b/devops-client/lib/devops-client/options/project_options.rb @@ -3,7 +3,7 @@ require "set" class ProjectOptions < CommonOptions - commands :create, :delete, :deploy, :list, {:multi => [:create]}, :servers, :stacks, {:set => [:run_list]}, :show, :test, :update, {:user => [:add, :delete]} + commands :create, :delete, :deploy, :list, {:multi => [:create]}, :servers, :stacks, {:set => [:run_list]}, :show, :test, :update, {:user => [:add, :delete]}, :delete_servers def initialize args, def_options super(args, def_options) @@ -23,6 +23,7 @@ class ProjectOptions < CommonOptions self.user_add_params = [id, "USER_NAME", "[USER_NAME ...]"] self.user_delete_params = [id, "USER_NAME", "[USER_NAME ...]"] self.test_params = [id, env] + self.delete_servers_params = [id, "[#{env}]"] end def create_options @@ -65,7 +66,6 @@ class ProjectOptions < CommonOptions # end end - end def user_add_options @@ -94,4 +94,10 @@ class ProjectOptions < CommonOptions end end + def delete_servers_options + self.options do |parser, options| + parser.recognize_option_value(:dry_run, 'project', type: :switch, default: false, switch_value: true, i18n_scope: 'delete_servers') + end + end + end diff --git a/devops-client/lib/devops-client/output/project.rb b/devops-client/lib/devops-client/output/project.rb index 256fd59..e6151cb 100644 --- a/devops-client/lib/devops-client/output/project.rb +++ b/devops-client/lib/devops-client/output/project.rb @@ -27,6 +27,8 @@ module Output when :test title = I18n.t("output.title.project.test", :project => ARGV[2], :env => ARGV[3]) create_test(@data) + when :delete_servers + return delete_servers_output else title = I18n.t("output.title.project.list") create_list(@data) @@ -149,5 +151,31 @@ module Output headers_and_rows(stacks, fields_to_output) end + def delete_servers_output + output = '' + if @options[:dry_run] + if @data['to_delete'].empty? + output << "There are no servers to delete." + else + output << "Servers to delete:\n----\n" + output << @data['to_delete'].join("\n") + end + return output + end + + if @data['deleted'].empty? + output << 'There are no deleted servers.' + else + output << "Deleted servers:\n----\n" + output << @data['deleted'].join("\n") + end + + if !@data['failed'].empty? + output << "\nThere were errors during deleting these servers:\n----\n" + output << @data['failed'].join("\n") + end + output + end + end end diff --git a/devops-client/locales/en.yml b/devops-client/locales/en.yml index 7e61cf8..58f1d9f 100644 --- a/devops-client/locales/en.yml +++ b/devops-client/locales/en.yml @@ -343,6 +343,8 @@ en: deploy_env: "Delete user from deploy environment" deploy: servers: "Servers list (comma separated) for deploy" + delete_servers: + dry_run: "Only show servers to delete, no real deleting" script: params: Script arguments (comma separated list) server: diff --git a/devops-service/app/api2/handlers/project.rb b/devops-service/app/api2/handlers/project.rb index 68d0b39..c771eae 100644 --- a/devops-service/app/api2/handlers/project.rb +++ b/devops-service/app/api2/handlers/project.rb @@ -3,6 +3,8 @@ require "db/mongo/models/project" require "workers/project_test_worker" require "app/api2/parsers/project" require "lib/project/type/types_factory" +require "lib/executors/server_executor" + require_relative "../helpers/version_2.rb" require_relative "request_handler" @@ -298,6 +300,32 @@ module Devops return [jid] end + def delete_project_servers(project_id) + env_id, dry_run = parser.delete_project_servers + Devops::Db.connector.project(project_id) + servers = Devops::Db.connector.servers(project_id, env_id) + info = {to_delete: servers.map(&:id)} + if !dry_run + info.merge!(delete_chosen_servers!(servers)) + end + info + end + + private + + def delete_chosen_servers!(servers) + deleted, failed = [], [] + servers.each do |server| + begin + Devops::Executor::ServerExecutor.new(server, '').delete_server + deleted << server.id + rescue + failed << server.id + end + end + {deleted: deleted, failed: failed} + end + end end end diff --git a/devops-service/app/api2/helpers/parser.rb b/devops-service/app/api2/helpers/parser.rb index ebf0d5e..dc5b4d3 100644 --- a/devops-service/app/api2/helpers/parser.rb +++ b/devops-service/app/api2/helpers/parser.rb @@ -28,6 +28,14 @@ module Devops check_param val, String, msg, _nil, empty end + def check_boolean val, msg + begin + check_param val, TrueClass, msg, false, true + rescue + check_param val, FalseClass, msg, false, true + end + end + def check_array val, msg, vals_type=String, _nil=false, empty=false check_param val, Array, msg, _nil, empty val.each {|v| raise Devops::ValidationError.new(msg) unless v.is_a?(vals_type)} unless val.nil? @@ -60,7 +68,7 @@ module Devops end end if val.is_a?(type) - raise Devops::ValidationError.new(msg) if val.empty? and !empty + raise Devops::ValidationError.new(msg) if !empty && val.empty? val else raise Devops::ValidationError.new(msg) diff --git a/devops-service/app/api2/parsers/project.rb b/devops-service/app/api2/parsers/project.rb index dc2a8e9..08b0e4c 100644 --- a/devops-service/app/api2/parsers/project.rb +++ b/devops-service/app/api2/parsers/project.rb @@ -97,6 +97,13 @@ module Devops servers = check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true) return deploy_env, servers end + + def delete_project_servers + body = create_object_from_json_body + dry_run = check_boolean(body["dry_run"], "Parameter 'dry_run' must be a boolean") + deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string", true) + [deploy_env, dry_run] + end end end end diff --git a/devops-service/app/api2/routes/project.rb b/devops-service/app/api2/routes/project.rb index edb48c1..7b66b61 100644 --- a/devops-service/app/api2/routes/project.rb +++ b/devops-service/app/api2/routes/project.rb @@ -166,10 +166,37 @@ module Devops # "id": "nstance id" # } # ] - app.get_with_headers "/project/:project/servers", :headers => [:accept] do |project| + servers_routes_hash = {} + servers_routes_hash["GET"] = lambda { |project| check_privileges("project", "r") json Devops::API2_0::Handler::Project.new(request).project_servers(project).map(&:to_hash) - end + } + + # Deletes project servers + # + # * *Request* + # - method : PATCH + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "dry_run": false # set to true if you'd like to just see list of servers to delete + # "deploy_env": null # set to env's identifier to delete that env's servers + # } + # + # * *Returns* : + # 200 - + # { + # "to_delete": ['server1', 'server2'], + # "deleted": ['server1'], + # "failed": ['server2'] + # } + servers_routes_hash["DELETE"] = lambda { |project_id| + check_privileges("project", "w") + json Devops::API2_0::Handler::Project.new(request).delete_project_servers(project_id) + } + app.multi_routes "/project/:project/servers", {}, servers_routes_hash # Get project stacks # diff --git a/devops-service/lib/executors/server_executor.rb b/devops-service/lib/executors/server_executor.rb index 9532cf4..cb983d1 100644 --- a/devops-service/lib/executors/server_executor.rb +++ b/devops-service/lib/executors/server_executor.rb @@ -116,7 +116,9 @@ module Devops return 0 end rescue => e - @out << e.message + @out.puts e.message + @out.puts e.backtrace.join("\n") + DevopsLogger.logger.error e.message roll_back mongo.server_delete @server.id diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 52644f8..3711e35 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -110,7 +110,8 @@ module Provider vpcId = nil unless subnets.empty? options["SubnetId"] = subnets[0] - vpcId = self.networks.detect{|n| n["name"] == options["SubnetId"]}["vpcId"] + network = self.networks.detect{|n| n["name"] == options["SubnetId"]} + vpcId = network["vpcId"] if network if vpcId.nil? out << "Can not get 'vpcId' by subnet name '#{options["SubnetId"]}'\n" return false From 1c9fca2017156cd3ab413c31fd4ce3792c77792a Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 13 Nov 2015 18:49:32 +0300 Subject: [PATCH 03/18] add ability to use blank password in devops-client conf --- devops-client/lib/devops-client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devops-client/lib/devops-client.rb b/devops-client/lib/devops-client.rb index 5004cf1..fd6c6e7 100644 --- a/devops-client/lib/devops-client.rb +++ b/devops-client/lib/devops-client.rb @@ -33,7 +33,7 @@ module DevopsClient if config[:host].nil? abort(I18n.t("config.invalid.host"), :file => @@config_file) end - [:api, :username, :password].each do |key| + [:api, :username].each do |key| if config[key].nil? or config[key].empty? abort(I18n.t("config.invalid.empty", :file => @@config_file, :key => key)) end From 8ea70f707231541f098350412899825f330ce3c6 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 13 Nov 2015 19:44:00 +0300 Subject: [PATCH 04/18] #871: ask questions before deleting --- .../lib/devops-client/handler/project.rb | 23 ++++++++++++++++++- .../lib/devops-client/output/project.rb | 9 -------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/devops-client/lib/devops-client/handler/project.rb b/devops-client/lib/devops-client/handler/project.rb index bf6b7aa..dd261d3 100644 --- a/devops-client/lib/devops-client/handler/project.rb +++ b/devops-client/lib/devops-client/handler/project.rb @@ -451,11 +451,32 @@ protected abort(error) end + ask_for_delete_servers(project, env) body = { deploy_env: env, - dry_run: @options[:dry_run] + dry_run: false } @list = delete("/project/#{project}/servers", body) end + private + + def ask_for_delete_servers(project, env) + body = { + deploy_env: env, + dry_run: true + } + to_delete = delete("/project/#{project}/servers", body)['to_delete'] + if to_delete.empty? + abort "There are no servers to delete." + else + puts "Servers to delete:\n----\n" + puts to_delete.join("\n") + puts '----' + end + if @options[:dry_run] || !question('Are you sure to delete them? ') + abort + end + end + end diff --git a/devops-client/lib/devops-client/output/project.rb b/devops-client/lib/devops-client/output/project.rb index e6151cb..b137aad 100644 --- a/devops-client/lib/devops-client/output/project.rb +++ b/devops-client/lib/devops-client/output/project.rb @@ -153,15 +153,6 @@ module Output def delete_servers_output output = '' - if @options[:dry_run] - if @data['to_delete'].empty? - output << "There are no servers to delete." - else - output << "Servers to delete:\n----\n" - output << @data['to_delete'].join("\n") - end - return output - end if @data['deleted'].empty? output << 'There are no deleted servers.' From 126caa26822f808f25b7ebf7ab8e4bf90e13fcac Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 13:23:13 +0300 Subject: [PATCH 05/18] #891: create stack events --- devops-service/commands/stack.rb | 1 + devops-service/db/mongo/models/stack/stack_base.rb | 3 ++- devops-service/providers/base_provider.rb | 4 ++++ devops-service/providers/ec2.rb | 4 ++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/devops-service/commands/stack.rb b/devops-service/commands/stack.rb index 3c45863..4508fea 100644 --- a/devops-service/commands/stack.rb +++ b/devops-service/commands/stack.rb @@ -24,6 +24,7 @@ module StackCommands sleep_times.each do |sleep_time| sleep sleep_time stack.sync_details! + out.puts stack.events.inspect case stack.stack_status when 'CREATE_IN_PROGRESS' out << "." diff --git a/devops-service/db/mongo/models/stack/stack_base.rb b/devops-service/db/mongo/models/stack/stack_base.rb index b8cb4c6..015ff94 100644 --- a/devops-service/db/mongo/models/stack/stack_base.rb +++ b/devops-service/db/mongo/models/stack/stack_base.rb @@ -7,7 +7,7 @@ module Devops include ModelWithProvider - attr_accessor :id, :name, :project, :deploy_env, :stack_template, :parameters, :details, :owner, :run_list + attr_accessor :id, :name, :project, :deploy_env, :stack_template, :parameters, :details, :events, :owner, :run_list types id: {type: String, empty: false}, provider: {type: String, empty: false}, @@ -67,6 +67,7 @@ module Devops def sync_details! self.details = provider_instance.stack_details(self) + self.events = provider_instance.stack_events(self) end def resources diff --git a/devops-service/providers/base_provider.rb b/devops-service/providers/base_provider.rb index bf9ef34..5ba85a4 100644 --- a/devops-service/providers/base_provider.rb +++ b/devops-service/providers/base_provider.rb @@ -43,5 +43,9 @@ module Provider def set_stack_tags stack, out="" end + def stack_events(stack) + [] + end + end end diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 52644f8..7a13e62 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -294,6 +294,10 @@ module Provider cloud_formation.describe_stack_resources({'StackName' => stack.name}).body['StackResources'] end + def stack_events(stack) + cloud_formation.describe_stack_events(stack.name).body['StackEvents'].map{|se| {"stack_name" => se["StackName"], "stack_id" => se["StackId"], "event_id" => se["EventId"], "reason" => se["ResourceStatusReason"]}} + end + # не работает, не используется # def stack_resource(stack, resource_id) # physical_id = fog_stack(stack).resources.get(resource_id).physical_resource_id From 645e0cbf24e9dc28e51d5b98ec70f2d16cfab061 Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 14:24:55 +0300 Subject: [PATCH 06/18] #891: events output to log --- devops-service/commands/stack.rb | 8 +++++++- devops-service/providers/ec2.rb | 2 +- devops-service/workers/worker.rb | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/devops-service/commands/stack.rb b/devops-service/commands/stack.rb index 4508fea..637109f 100644 --- a/devops-service/commands/stack.rb +++ b/devops-service/commands/stack.rb @@ -21,10 +21,16 @@ module StackCommands begin out << "Syncing stack '#{stack.id}'...\n" + events_keys = [] sleep_times.each do |sleep_time| sleep sleep_time stack.sync_details! - out.puts stack.events.inspect + stack.events.each do |event| + unless events_keys.include?(event["event_id"]) + events_keys << event["event_id"] + out.puts "#{event["status"]}: #{event["reason"]}" + end + end case stack.stack_status when 'CREATE_IN_PROGRESS' out << "." diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 7a13e62..4cb3d9f 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -295,7 +295,7 @@ module Provider end def stack_events(stack) - cloud_formation.describe_stack_events(stack.name).body['StackEvents'].map{|se| {"stack_name" => se["StackName"], "stack_id" => se["StackId"], "event_id" => se["EventId"], "reason" => se["ResourceStatusReason"]}} + cloud_formation.describe_stack_events(stack.name).body['StackEvents'].map{|se| {"stack_name" => se["StackName"], "stack_id" => se["StackId"], "event_id" => se["EventId"], "reason" => se["ResourceStatusReason"], "status" => se["ResourceStatus"]}} end # не работает, не используется diff --git a/devops-service/workers/worker.rb b/devops-service/workers/worker.rb index 3c80a63..4b130e5 100644 --- a/devops-service/workers/worker.rb +++ b/devops-service/workers/worker.rb @@ -91,6 +91,7 @@ class Worker self.out = out job_result = yield(out, file) + out.puts "TODO: job_result: #{job_result}" canonical_status = (job_result == 0 ? STATUS::COMPLETED : STATUS::FAILED) update_job_status(canonical_status, job_result) rescue StandardError, RecordNotFound => e From c0b932e749e7e33a48bdd015da1878742d1e1edc Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 14:43:23 +0300 Subject: [PATCH 07/18] #891: for test --- devops-service/commands/stack.rb | 15 ++++++++------- devops-service/workers/stack_bootstrap_worker.rb | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/devops-service/commands/stack.rb b/devops-service/commands/stack.rb index 637109f..c841967 100644 --- a/devops-service/commands/stack.rb +++ b/devops-service/commands/stack.rb @@ -1,13 +1,15 @@ module StackCommands extend self + RESULT_CODES = { + stack_rolled_back: 1, + unkown_status: 2, + timeout: 3, + error: 5 + } + def self.result_codes - { - stack_rolled_back: 1, - unkown_status: 2, - timeout: 3, - error: 5 - } + RESULT_CODES end def self.result_code(code) @@ -33,7 +35,6 @@ module StackCommands end case stack.stack_status when 'CREATE_IN_PROGRESS' - out << "." out.flush when 'CREATE_COMPLETE' mongo.stack_update(stack) diff --git a/devops-service/workers/stack_bootstrap_worker.rb b/devops-service/workers/stack_bootstrap_worker.rb index 442e793..ee5cc1c 100644 --- a/devops-service/workers/stack_bootstrap_worker.rb +++ b/devops-service/workers/stack_bootstrap_worker.rb @@ -23,6 +23,7 @@ class StackBootstrapWorker < Worker begin stack = create_stack(stack_attrs) rescue StackCreatingError + @out.puts "Stack creating error" return 1 end From e4f7f387f354aba9442410b83b35068baf6a8d29 Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 14:57:15 +0300 Subject: [PATCH 08/18] #891: for testing 2 --- devops-service/workers/worker.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devops-service/workers/worker.rb b/devops-service/workers/worker.rb index 4b130e5..69e2ee8 100644 --- a/devops-service/workers/worker.rb +++ b/devops-service/workers/worker.rb @@ -93,6 +93,8 @@ class Worker job_result = yield(out, file) out.puts "TODO: job_result: #{job_result}" canonical_status = (job_result == 0 ? STATUS::COMPLETED : STATUS::FAILED) + out.puts "TODO: status: #{canonical_status}" + DevopsLogger.logger.info "TODO: res: #{job_result} status: #{canonical_status}" update_job_status(canonical_status, job_result) rescue StandardError, RecordNotFound => e out << "\n #{e.class}\n #{e.message}\n" From ceacf075940f79c35ac24fb77f6aacba3f4eefc5 Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 16:21:28 +0300 Subject: [PATCH 09/18] #891: for testing 3 --- devops-service/db/mongo/connectors/report.rb | 5 +- .../workers/stack_bootstrap_worker.rb | 52 +++++++++---------- devops-service/workers/worker.rb | 5 +- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/devops-service/db/mongo/connectors/report.rb b/devops-service/db/mongo/connectors/report.rb index fcc363f..0a8d449 100644 --- a/devops-service/db/mongo/connectors/report.rb +++ b/devops-service/db/mongo/connectors/report.rb @@ -59,10 +59,7 @@ module Connectors set = {"status" => status, "updated_at" => Time.new} set["job_result_code"] = job_result_code unless job_result_code.nil? collection.update({"_id" => jid}, {"$set" => set}) - end - - def set_report_status(jid, status, job_result_code) - collection.update({"_id" => jid}, {"$set" => {"status" => status, "updated_at" => Time.new, "job_result_code" => job_result_code}}) + DevopsLogger.logger.info("Report '#{jid}' status has been changed to '#{status}'") end def set_report_server_data id, chef_node_name, host diff --git a/devops-service/workers/stack_bootstrap_worker.rb b/devops-service/workers/stack_bootstrap_worker.rb index ee5cc1c..c7298b0 100644 --- a/devops-service/workers/stack_bootstrap_worker.rb +++ b/devops-service/workers/stack_bootstrap_worker.rb @@ -22,35 +22,35 @@ class StackBootstrapWorker < Worker begin stack = create_stack(stack_attrs) + + #TODO: errors + begin + servers_with_priority = persist_stack_servers!(stack) + unless without_bootstrap + sorted_keys = servers_with_priority.keys.sort{|x,y| y <=> x} + sorted_keys.each do |key| + @out.puts "Servers with priority '#{key}':" + bootstrap_servers!(servers_with_priority[key], report) + end + end + @out.puts "Done." + 0 + rescue BootstrapingStackServerError + @out.puts "\nAn error occured during bootstraping stack servers. Initiating stack rollback." + rollback_stack!(stack) + 2 + rescue DeployingStackServerError => e + @out.puts "\nStack was launched, but an error occured during deploying stack servers." + @out.puts "You can redeploy stack after fixing the error." + 3 + rescue StandardError => e + @out.puts "\nAn error occured. Initiating stack rollback." + rollback_stack!(stack) + raise e + end rescue StackCreatingError @out.puts "Stack creating error" - return 1 - end - - #TODO: errors - begin - servers_with_priority = persist_stack_servers!(stack) - unless without_bootstrap - sorted_keys = servers_with_priority.keys.sort{|x,y| y <=> x} - sorted_keys.each do |key| - @out.puts "Servers with priority '#{key}':" - bootstrap_servers!(servers_with_priority[key], report) - end - end - @out.puts "Done." - 0 - rescue BootstrapingStackServerError - @out.puts "\nAn error occured during bootstraping stack servers. Initiating stack rollback." - rollback_stack!(stack) 1 - rescue DeployingStackServerError => e - @out.puts "\nStack was launched, but an error occured during deploying stack servers." - @out.puts "You can redeploy stack after fixing the error." - 1 - rescue StandardError => e - @out.puts "\nAn error occured. Initiating stack rollback." - rollback_stack!(stack) - raise e end end end diff --git a/devops-service/workers/worker.rb b/devops-service/workers/worker.rb index 69e2ee8..a6ab79f 100644 --- a/devops-service/workers/worker.rb +++ b/devops-service/workers/worker.rb @@ -94,6 +94,7 @@ class Worker out.puts "TODO: job_result: #{job_result}" canonical_status = (job_result == 0 ? STATUS::COMPLETED : STATUS::FAILED) out.puts "TODO: status: #{canonical_status}" + out.flush DevopsLogger.logger.info "TODO: res: #{job_result} status: #{canonical_status}" update_job_status(canonical_status, job_result) rescue StandardError, RecordNotFound => e @@ -122,7 +123,7 @@ class Worker end def update_job_status(status, job_result_code) - set_status(jid, status) + Worker.set_status(jid, status) mongo.set_report_status(jid, status, job_result_code) status end @@ -133,8 +134,10 @@ class Worker config end +=begin def set_status id, status self.class.set_status(id, status) end +=end end From abc03bcae30a8145ba71aeadeb1681928e3d1e7d Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 16:30:56 +0300 Subject: [PATCH 10/18] #891: done --- devops-service/providers/provider_factory.rb | 2 +- devops-service/workers/worker.rb | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/devops-service/providers/provider_factory.rb b/devops-service/providers/provider_factory.rb index 921cceb..44ffa24 100644 --- a/devops-service/providers/provider_factory.rb +++ b/devops-service/providers/provider_factory.rb @@ -39,7 +39,7 @@ module Provider o = Provider.const_get(p.capitalize).new(conf) if o.configured? @@providers[p] = o - @@available_providers << p + ProviderFactory.add_provider p puts "Provider '#{p}' has been loaded" end factory = Provider.const_get(p.capitalize + "AccountsFactory").new diff --git a/devops-service/workers/worker.rb b/devops-service/workers/worker.rb index a6ab79f..eb5af3e 100644 --- a/devops-service/workers/worker.rb +++ b/devops-service/workers/worker.rb @@ -91,11 +91,7 @@ class Worker self.out = out job_result = yield(out, file) - out.puts "TODO: job_result: #{job_result}" canonical_status = (job_result == 0 ? STATUS::COMPLETED : STATUS::FAILED) - out.puts "TODO: status: #{canonical_status}" - out.flush - DevopsLogger.logger.info "TODO: res: #{job_result} status: #{canonical_status}" update_job_status(canonical_status, job_result) rescue StandardError, RecordNotFound => e out << "\n #{e.class}\n #{e.message}\n" @@ -134,10 +130,4 @@ class Worker config end -=begin - def set_status id, status - self.class.set_status(id, status) - end -=end - end From 7595f05709b0f64422394bee3ee83108a2106439 Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 16:46:30 +0300 Subject: [PATCH 11/18] #891: done --- devops-service/commands/stack.rb | 2 +- devops-service/providers/ec2.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devops-service/commands/stack.rb b/devops-service/commands/stack.rb index c841967..ba98ded 100644 --- a/devops-service/commands/stack.rb +++ b/devops-service/commands/stack.rb @@ -30,7 +30,7 @@ module StackCommands stack.events.each do |event| unless events_keys.include?(event["event_id"]) events_keys << event["event_id"] - out.puts "#{event["status"]}: #{event["reason"]}" + out.puts "#{event["timestamp"]} - #{event["status"]}: #{event["reason"]}" end end case stack.stack_status diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 4cb3d9f..944b6a0 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -295,7 +295,7 @@ module Provider end def stack_events(stack) - cloud_formation.describe_stack_events(stack.name).body['StackEvents'].map{|se| {"stack_name" => se["StackName"], "stack_id" => se["StackId"], "event_id" => se["EventId"], "reason" => se["ResourceStatusReason"], "status" => se["ResourceStatus"]}} + cloud_formation.describe_stack_events(stack.name).body['StackEvents'].map{|se| {"timestamp" => se["Timestamp"], "stack_name" => se["StackName"], "stack_id" => se["StackId"], "event_id" => se["EventId"], "reason" => se["ResourceStatusReason"], "status" => se["ResourceStatus"]}}.sort{|se1, se2| se1["timestamp"] <=> se2["timestamp"]} end # не работает, не используется From 1166e5a90cbf29c27ffe5bc06a746a1d3ded4ba5 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Tue, 17 Nov 2015 18:08:38 +0300 Subject: [PATCH 12/18] 813: add ability to change stack's stack_template --- .../lib/devops-client/handler/stack.rb | 13 +++++++++++++ .../devops-client/options/stack_options.rb | 3 ++- devops-service/app/api2/handlers/stack.rb | 7 +++++++ devops-service/app/api2/parsers/stack.rb | 6 ++++++ devops-service/app/api2/routes/stack.rb | 19 +++++++++++++++++++ .../db/mongo/models/stack/stack_ec2.rb | 17 +++++++++++++++++ devops-service/providers/ec2.rb | 4 ++++ 7 files changed, 68 insertions(+), 1 deletion(-) diff --git a/devops-client/lib/devops-client/handler/stack.rb b/devops-client/lib/devops-client/handler/stack.rb index b32b4b8..7385024 100644 --- a/devops-client/lib/devops-client/handler/stack.rb +++ b/devops-client/lib/devops-client/handler/stack.rb @@ -41,6 +41,9 @@ class Stack < Handler else output end + when :change_stack_template + change_stack_template_handler + output end end @@ -136,6 +139,16 @@ class Stack < Handler puts response.inspect end + def change_stack_template_handler + stack_id, template_id = @args[2], @args[3] + r = inspect_parameters(@options_parser.delete_params, stack_id, template_id) + unless r.nil? + @options_parser.invalid_change_stack_template_command + abort(r) + end + @show = put("/stack/#{stack_id}/stack_template", {stack_template: template_id}) + end + private def provider_stacks(provider) diff --git a/devops-client/lib/devops-client/options/stack_options.rb b/devops-client/lib/devops-client/options/stack_options.rb index 2bfd266..c33c25a 100644 --- a/devops-client/lib/devops-client/options/stack_options.rb +++ b/devops-client/lib/devops-client/options/stack_options.rb @@ -2,7 +2,7 @@ require "devops-client/options/common_options" class StackOptions < CommonOptions - commands :create, :delete, :list, :show, :sync, :resources, :deploy, :reserve, :unreserve + commands :create, :delete, :list, :show, :sync, :resources, :deploy, :reserve, :unreserve, :change_stack_template def initialize args, def_options super(args, def_options) @@ -15,6 +15,7 @@ class StackOptions < CommonOptions self.resources_params = ["STACK"] self.deploy_params = ["STACK"] self.reserve_params = ["STACK"] + self.change_stack_template_params = %w(STACK STACK_TEMPLATE) end def create_options diff --git a/devops-service/app/api2/handlers/stack.rb b/devops-service/app/api2/handlers/stack.rb index ffc1fe0..c6b122d 100644 --- a/devops-service/app/api2/handlers/stack.rb +++ b/devops-service/app/api2/handlers/stack.rb @@ -203,6 +203,13 @@ module Devops end end + def change_stack_template(stack_id) + stack_template_id = parser.change_stack_template + Devops::Db.connector.stack_template(stack_template_id) + stack = Devops::Db.connector.stack(stack_id) + stack.change_stack_template!(stack_template_id) + end + end end end diff --git a/devops-service/app/api2/parsers/stack.rb b/devops-service/app/api2/parsers/stack.rb index c9d5c64..de1ee1c 100644 --- a/devops-service/app/api2/parsers/stack.rb +++ b/devops-service/app/api2/parsers/stack.rb @@ -38,6 +38,12 @@ module Devops @body end + def change_stack_template + template_id = create_object_from_json_body['stack_template'] + check_string(template_id, "Parameter 'stack_template' must be a not empty string") + template_id + end + end end end diff --git a/devops-service/app/api2/routes/stack.rb b/devops-service/app/api2/routes/stack.rb index 37f3413..61ac8ad 100644 --- a/devops-service/app/api2/routes/stack.rb +++ b/devops-service/app/api2/routes/stack.rb @@ -187,6 +187,25 @@ module Devops } app.multi_routes "/stack/:id/tags", {:headers => [:content_type]}, hash + # Change stack stack_template + # + # * *Request* + # - method : PUT + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # - body : + # { + # "stack_template": "template_id" + # } + # + # * *Returns* : + # 200 - OK + app.put_with_headers "/stack/:id/stack_template", :headers => [:accept, :content_type] do |id| + check_privileges("stack", "r") + json Devops::API2_0::Handler::Stack.new(request).change_stack_template(id) + end + puts "Stack routes initialized" end diff --git a/devops-service/db/mongo/models/stack/stack_ec2.rb b/devops-service/db/mongo/models/stack/stack_ec2.rb index 32111c1..588a631 100644 --- a/devops-service/db/mongo/models/stack/stack_ec2.rb +++ b/devops-service/db/mongo/models/stack/stack_ec2.rb @@ -18,6 +18,23 @@ module Devops result end + def update_in_cloud!(params) + parameters = params.keep_if do |key, value| + %w(Parameters TemplateBody TemplateURL Capabilities) + end + + provider_instance.update_stack(self, parameters) + end + + # We should improve this functionality: stack will be rollbacked + # if there are errors during stack_template changing, so we should track + # that process and persist changes only in case of success. + def change_stack_template!(stack_template) + self.stack_template = stack_template + update_in_cloud!('TemplateBody' => template_body) + Devops::Db.connector.stack_update(self) + end + end end end diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index f8ff36d..c9e97f1 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -261,6 +261,10 @@ module Provider end end + def update_stack(stack, params) + cloud_formation.update_stack(stack.name, params) + end + def set_stack_tags stack, out="" tags = { # "cid:remoteUser" => s.remote_user From 391f2add9c5eb584d4c7e755c2d1c098048bed0e Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Wed, 18 Nov 2015 22:45:39 +0300 Subject: [PATCH 13/18] fix params filtering in StackEc2#update_in_cloud! --- devops-service/db/mongo/models/stack/stack_ec2.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devops-service/db/mongo/models/stack/stack_ec2.rb b/devops-service/db/mongo/models/stack/stack_ec2.rb index 588a631..184a7fd 100644 --- a/devops-service/db/mongo/models/stack/stack_ec2.rb +++ b/devops-service/db/mongo/models/stack/stack_ec2.rb @@ -19,8 +19,8 @@ module Devops end def update_in_cloud!(params) - parameters = params.keep_if do |key, value| - %w(Parameters TemplateBody TemplateURL Capabilities) + parameters = params.keep_if do |key| + %w(Parameters TemplateBody TemplateURL Capabilities).include?(key) end provider_instance.update_stack(self, parameters) From e81bc64130510715ef6ab8b406242cbb3f4040c9 Mon Sep 17 00:00:00 2001 From: Anton Martynov Date: Fri, 20 Nov 2015 12:04:59 +0300 Subject: [PATCH 14/18] #840: chef_environment support --- devops-service/lib/executors/server_executor.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/devops-service/lib/executors/server_executor.rb b/devops-service/lib/executors/server_executor.rb index 9532cf4..1ba4c1f 100644 --- a/devops-service/lib/executors/server_executor.rb +++ b/devops-service/lib/executors/server_executor.rb @@ -215,6 +215,7 @@ module Devops bootstrap_options.push "-N #{@server.chef_node_name}" if @server.chef_node_name bootstrap_options.push "--sudo" unless @server.remote_user == "root" bootstrap_options.push "-t #{options[:bootstrap_template]}" if options[:bootstrap_template] + bootstrap_options.push "-E #{options[:chef_environment]}" if options[:chef_environment] rl = options[:run_list] bootstrap_options.push "-r #{rl.join(",")}" unless rl.nil? or rl.empty? bootstrap_options.push "-c #{options[:config]}" if options[:config] From 65b2e02fe9615b439de433462a69cc426188f576 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Tue, 3 Nov 2015 11:46:54 +0300 Subject: [PATCH 15/18] Set up rspec, add specs for models --- .gitignore | 1 + devops-service/.rspec | 3 + devops-service/Gemfile | 5 + devops-service/Gemfile.lock | 44 ++++ devops-service/Guardfile | 44 ++++ devops-service/core/devops-application.rb | 2 +- .../models/deploy_env/cloud_deploy_env.rb | 14 -- .../models/deploy_env/deploy_env_base.rb | 2 +- .../mongo/models/deploy_env/deploy_env_ec2.rb | 2 - .../models/deploy_env/deploy_env_openstack.rb | 22 +- .../models/deploy_env/deploy_env_static.rb | 1 + devops-service/db/mongo/models/image.rb | 11 +- devops-service/db/mongo/models/key.rb | 3 +- devops-service/db/mongo/models/mongo_model.rb | 9 + devops-service/db/mongo/models/server.rb | 13 +- .../db/mongo/models/stack/stack_base.rb | 36 ++- .../db/mongo/models/stack/stack_ec2.rb | 5 +- .../db/mongo/models/stack/stack_openstack.rb | 1 + .../stack_template/stack_template_base.rb | 17 ++ .../stack_template/stack_template_ec2.rb | 2 +- devops-service/db/mongo/models/user.rb | 25 +-- devops-service/db/validators/base.rb | 2 + .../db/validators/deploy_env/flavor.rb | 9 +- .../db/validators/deploy_env/groups.rb | 11 +- .../db/validators/deploy_env/image.rb | 10 +- .../validators/deploy_env/stack_template.rb | 21 +- .../db/validators/field_validators/flavor.rb | 8 +- .../db/validators/field_validators/image.rb | 9 +- devops-service/db/validators/helpers/users.rb | 9 +- .../db/validators/image/bootstrap_template.rb | 9 +- .../db/validators/image/image_in_filter.rb | 9 +- devops-service/spec/factories/deploy_env.rb | 28 +++ devops-service/spec/factories/image.rb | 10 + devops-service/spec/factories/key.rb | 8 + devops-service/spec/factories/report.rb | 20 ++ devops-service/spec/factories/server.rb | 19 ++ devops-service/spec/factories/stack.rb | 24 ++ .../spec/factories/stack_template.rb | 20 ++ devops-service/spec/factories/user.rb | 9 + .../models/deploy_env/deploy_env_ec2_spec.rb | 66 ++++++ .../deploy_env/deploy_env_openstack_spec.rb | 32 +++ .../deploy_env/deploy_env_static_spec.rb | 19 ++ devops-service/spec/models/image_spec.rb | 47 ++++ devops-service/spec/models/key_spec.rb | 28 +++ devops-service/spec/models/report_spec.rb | 43 ++++ devops-service/spec/models/server_spec.rb | 95 ++++++++ .../spec/models/stack/stack_ec2_spec.rb | 174 +++++++++++++++ .../stack_template/stack_template_ec2_spec.rb | 13 ++ .../stack_template_openstack_spec.rb | 13 ++ devops-service/spec/models/user_spec.rb | 107 +++++++++ devops-service/spec/spec_helper.rb | 78 +++++++ devops-service/spec/support/blank_file | 0 .../support/shared_cloud_deploy_env_specs.rb | 38 ++++ .../spec/support/shared_deploy_env_specs.rb | 211 ++++++++++++++++++ .../support/shared_stack_template_specs.rb | 53 +++++ .../spec/support/shared_validation_specs.rb | 121 ++++++++++ devops-service/spec/support/spec_support.rb | 11 + 57 files changed, 1551 insertions(+), 95 deletions(-) create mode 100644 devops-service/.rspec create mode 100644 devops-service/Guardfile create mode 100644 devops-service/spec/factories/deploy_env.rb create mode 100644 devops-service/spec/factories/image.rb create mode 100644 devops-service/spec/factories/key.rb create mode 100644 devops-service/spec/factories/report.rb create mode 100644 devops-service/spec/factories/server.rb create mode 100644 devops-service/spec/factories/stack.rb create mode 100644 devops-service/spec/factories/stack_template.rb create mode 100644 devops-service/spec/factories/user.rb create mode 100644 devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb create mode 100644 devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb create mode 100644 devops-service/spec/models/deploy_env/deploy_env_static_spec.rb create mode 100644 devops-service/spec/models/image_spec.rb create mode 100644 devops-service/spec/models/key_spec.rb create mode 100644 devops-service/spec/models/report_spec.rb create mode 100644 devops-service/spec/models/server_spec.rb create mode 100644 devops-service/spec/models/stack/stack_ec2_spec.rb create mode 100644 devops-service/spec/models/stack_template/stack_template_ec2_spec.rb create mode 100644 devops-service/spec/models/stack_template/stack_template_openstack_spec.rb create mode 100644 devops-service/spec/models/user_spec.rb create mode 100644 devops-service/spec/spec_helper.rb create mode 100644 devops-service/spec/support/blank_file create mode 100644 devops-service/spec/support/shared_cloud_deploy_env_specs.rb create mode 100644 devops-service/spec/support/shared_deploy_env_specs.rb create mode 100644 devops-service/spec/support/shared_stack_template_specs.rb create mode 100644 devops-service/spec/support/shared_validation_specs.rb create mode 100644 devops-service/spec/support/spec_support.rb diff --git a/.gitignore b/.gitignore index 862df22..49cc974 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ devops-service/tests/features/support/config.yml .devops_files/ devops-service/plugins +devops-service/spec/examples.txt \ No newline at end of file diff --git a/devops-service/.rspec b/devops-service/.rspec new file mode 100644 index 0000000..97c5f28 --- /dev/null +++ b/devops-service/.rspec @@ -0,0 +1,3 @@ +--color +--format progress +--require spec_helper \ No newline at end of file diff --git a/devops-service/Gemfile b/devops-service/Gemfile index 7cc7ac9..fe1c3b9 100644 --- a/devops-service/Gemfile +++ b/devops-service/Gemfile @@ -23,12 +23,17 @@ gem 'hooks' #gem "devops-nibr", :path => "../../devops-nibr" + group :test do gem 'cucumber' gem 'test-unit' gem 'httpclient' + gem 'rspec', '~>3.3' + gem 'factory_girl', '~>4.5' + gem 'activesupport' end group :devepoment do gem 'byebug' + gem 'guard-rspec', require: false end diff --git a/devops-service/Gemfile.lock b/devops-service/Gemfile.lock index 173fa21..bac5127 100644 --- a/devops-service/Gemfile.lock +++ b/devops-service/Gemfile.lock @@ -2,6 +2,12 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (2.3.1) + activesupport (4.2.4) + i18n (~> 0.7) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) addressable (2.3.8) backports (3.6.4) bson (1.12.3) @@ -60,6 +66,8 @@ GEM erubis (2.7.0) eventmachine (1.0.7) excon (0.45.3) + factory_girl (4.5.0) + activesupport (>= 3.0.0) ffi (1.9.9) ffi-yajl (1.4.0) ffi (~> 1.5) @@ -163,18 +171,38 @@ GEM formatador (0.2.5) gherkin (2.12.2) multi_json (~> 1.3) + guard (2.13.0) + formatador (>= 0.2.4) + listen (>= 2.7, <= 4.0) + lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-compat (1.2.1) + guard-rspec (4.6.4) + guard (~> 2.1) + guard-compat (~> 1.1) + rspec (>= 2.99.0, < 4.0) hashie (2.1.2) highline (1.7.2) hooks (0.4.0) uber (~> 0.0.4) httpclient (2.6.0.1) + i18n (0.7.0) inflecto (0.0.2) ipaddress (0.8.0) json (1.8.3) libyajl2 (1.2.0) + listen (3.0.4) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9) + lumberjack (1.0.9) method_source (0.8.2) mime-types (1.25.1) mini_portile (0.6.2) + minitest (5.8.2) mixlib-authentication (1.3.0) mixlib-log mixlib-cli (1.5.0) @@ -185,6 +213,7 @@ GEM bson (= 1.12.3) multi_json (1.7.8) multi_test (0.1.2) + nenv (0.2.0) net-dhcp (1.3.2) net-scp (1.2.1) net-ssh (>= 2.6.5) @@ -196,6 +225,9 @@ GEM net-ssh-gateway (>= 1.2.0) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) + notiffany (0.0.8) + nenv (~> 0.1) + shellany (~> 0.0) ohai (8.0.1) ffi (~> 1.9) ffi-yajl (~> 1.1) @@ -222,6 +254,9 @@ GEM rack-test (0.6.3) rack (>= 1.0) rake (10.2.0) + rb-fsevent (0.9.6) + rb-inotify (0.9.5) + ffi (>= 0.5.0) redis (3.2.1) redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) @@ -249,6 +284,7 @@ GEM rspec (~> 3.0) rspec-its specinfra (~> 2.35) + shellany (0.0.1) sidekiq (3.2.6) celluloid (= 0.15.2) connection_pool (>= 2.0.0) @@ -281,8 +317,12 @@ GEM daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) + thor (0.19.1) + thread_safe (0.3.5) tilt (1.4.1) timers (1.1.0) + tzinfo (1.2.2) + thread_safe (~> 0.1) uber (0.0.13) uuidtools (2.1.5) wisper (1.6.0) @@ -292,11 +332,14 @@ PLATFORMS ruby DEPENDENCIES + activesupport bson_ext byebug chef (>= 12) cucumber + factory_girl (~> 4.5) fog (~> 1.20) + guard-rspec hooks httpclient mime-types (~> 1.25.1) @@ -306,6 +349,7 @@ DEPENDENCIES rack (= 1.5.2) rack-accept-media-types rake (= 10.2.0) + rspec (~> 3.3) sidekiq (= 3.2.6) sinatra (= 1.4.5) sinatra-contrib diff --git a/devops-service/Guardfile b/devops-service/Guardfile new file mode 100644 index 0000000..9248c74 --- /dev/null +++ b/devops-service/Guardfile @@ -0,0 +1,44 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +## Uncomment and set this to only include directories you want to watch +# directories %w(app lib config test spec features) \ +# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")} + +## Note: if you are using the `directories` clause above and you are not +## watching the project directory ('.'), then you will want to move +## the Guardfile to a watched dir and symlink it back, e.g. +# +# $ mkdir config +# $ mv Guardfile config/ +# $ ln -s config/Guardfile . +# +# and, you'll have to watch "config/Guardfile" instead of "Guardfile" + +# Note: The cmd option is now required due to the increasing number of ways +# rspec may be run, below are examples of the most common uses. +# * bundler: 'bundle exec rspec' +# * bundler binstubs: 'bin/rspec' +# * spring: 'bin/rspec' (This will use spring if running and you have +# installed the spring binstubs per the docs) +# * zeus: 'zeus rspec' (requires the server to be started separately) +# * 'just' rspec: 'rspec' + +guard :rspec, cmd: "rspec" do + require "guard/rspec/dsl" + dsl = Guard::RSpec::Dsl.new(self) + + # Feel free to open issues for suggestions and improvements + + # RSpec files + rspec = dsl.rspec + watch(rspec.spec_helper) { rspec.spec_dir } + watch(rspec.spec_support) { rspec.spec_dir } + watch(rspec.spec_files) + + # Factories files + watch(%r{spec/factories/.+\.rb}) { rspec.spec_dir } + + # Devops files + watch(%r{db/.+\.rb}) { rspec.spec_dir } +end diff --git a/devops-service/core/devops-application.rb b/devops-service/core/devops-application.rb index cfe09d1..15800b7 100644 --- a/devops-service/core/devops-application.rb +++ b/devops-service/core/devops-application.rb @@ -5,7 +5,7 @@ module Devops def self.inherited(base) @@applications << base.new - puts "Devops application '#{base}' has been added" + puts "Devops application '#{base}' has been added" unless ENV['RACK_ENV'] == 'test' end def self.applications diff --git a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb index 30adbf2..dc62b4d 100644 --- a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb +++ b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb @@ -12,21 +12,7 @@ module Devops class CloudDeployEnv < DeployEnvBase attr_accessor :flavor, :image, :subnets, :groups, :stack_template -=begin -@Deprecated - types :identifier => {:type => String, :empty => false}, - :image => {:type => String, :empty => false}, - :flavor => {:type => String, :empty => false}, - :provider => {:type => String, :empty => false}, - :expires => {:type => String, :empty => false, :nil => true}, - :run_list => {:type => Array, :empty => true}, - :users => {:type => Array, :empty => true}, - :subnets => {:type => Array, :empty => true}, - :groups => {:type => Array, :empty => false}, - :stack_template => {:type => String, :empty => false, :nil => true} -=end - #TODO: account validator set_validators ::Validators::DeployEnv::Flavor, ::Validators::DeployEnv::Image, ::Validators::DeployEnv::Groups, 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 b5aec77..0f2bfec 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 @@ -69,7 +69,7 @@ module Devops role = res[:new] || res[:exist] self.run_list << "role[#{role[0]}]" unless role.nil? Devops::Db.connector.set_project_deploy_env_field(project_id, old_name, {"identifier" => new_name, "run_list" => self.run_list}) - msg = "Project '#{project_id}': environment '#{old_name}' has been renamed to '#{new_name}'." + Project.create_roles_response(res) + msg = "Project '#{project_id}': environment '#{old_name}' has been renamed to '#{new_name}'." + ::Devops::Model::Project.create_roles_response(res) DevopsLogger.logger.info(msg) msg end 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 39a455a..22424e7 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 @@ -40,8 +40,6 @@ module Devops DeployEnvEc2.new(hash) end - private - def subnets_filter networks = provider_instance.networks 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 85687ad..e236b8e 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 @@ -4,16 +4,16 @@ module Devops module Model class DeployEnvOpenstack < CloudDeployEnv - types :identifier => {:type => String, :empty => false}, - :image => {:type => String, :empty => false}, - :flavor => {:type => String, :empty => false}, - :provider => {:type => String, :empty => false}, - :expires => {:type => String, :empty => false, :nil => true}, - :run_list => {:type => Array, :empty => true}, - :users => {:type => Array, :empty => true}, - :subnets => {:type => Array, :empty => true}, - :groups => {:type => Array, :empty => false}, - :stack_template => {:type => String, :empty => false, :nil => true} + # types :identifier => {:type => String, :empty => false}, + # :image => {:type => String, :empty => false}, + # :flavor => {:type => String, :empty => false}, + # :provider => {:type => String, :empty => false}, + # :expires => {:type => String, :empty => false, :nil => true}, + # :run_list => {:type => Array, :empty => true}, + # :users => {:type => Array, :empty => true}, + # :subnets => {:type => Array, :empty => true}, + # :groups => {:type => Array, :empty => false}, + # :stack_template => {:type => String, :empty => false, :nil => true} =begin set_validators ::Validators::DeployEnv::RunList, @@ -31,8 +31,6 @@ module Devops DeployEnvOpenstack.new(hash) end - private - def subnets_filter nil end 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 5635d4b..dc573a0 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 @@ -7,6 +7,7 @@ module Devops module Model class DeployEnvStatic < DeployEnvBase + # TODO: all code except self.create could be safely removed types :identifier => {:type => String, :empty => false}, :provider => {:type => String, :empty => false}, :expires => {:type => String, :empty => false, :nil => true}, diff --git a/devops-service/db/mongo/models/image.rb b/devops-service/db/mongo/models/image.rb index 728a66a..a5dd1da 100644 --- a/devops-service/db/mongo/models/image.rb +++ b/devops-service/db/mongo/models/image.rb @@ -65,13 +65,12 @@ module Devops end def to_hash_without_id - o = { - "provider" => self.provider, - "name" => self.name, - "remote_user" => self.remote_user + { + "provider" => provider, + "name" => name, + "remote_user" => remote_user, + "bootstrap_template" => bootstrap_template } - o["bootstrap_template"] = self.bootstrap_template - o end def self.create_from_json! json diff --git a/devops-service/db/mongo/models/key.rb b/devops-service/db/mongo/models/key.rb index 74184e4..32332ff 100644 --- a/devops-service/db/mongo/models/key.rb +++ b/devops-service/db/mongo/models/key.rb @@ -39,11 +39,10 @@ module Devops end def to_hash_without_id - o = { + { "path" => self.path, "scope" => self.scope } - o end end diff --git a/devops-service/db/mongo/models/mongo_model.rb b/devops-service/db/mongo/models/mongo_model.rb index 3721a5c..690576c 100644 --- a/devops-service/db/mongo/models/mongo_model.rb +++ b/devops-service/db/mongo/models/mongo_model.rb @@ -59,6 +59,7 @@ module Devops # TODO: we should validate type in request parser # self.validate_fields_types self.class.validate_model(self) + validate_fields! true rescue InvalidRecord => e error_message = self.build_error_message(e.message) @@ -81,6 +82,13 @@ module Devops true end + def valid? + validate! + true + rescue InvalidRecord + false + end + def build_error_message(message) # overrided in descendants message @@ -157,6 +165,7 @@ module Devops end end + # validate field value # if method validate! returns false, then stop validation without error def set_field_validators field, *validators diff --git a/devops-service/db/mongo/models/server.rb b/devops-service/db/mongo/models/server.rb index 91ec775..6b4bcaf 100644 --- a/devops-service/db/mongo/models/server.rb +++ b/devops-service/db/mongo/models/server.rb @@ -1,7 +1,7 @@ require "db/mongo/models/mongo_model" require "db/mongo/models/model_with_provider" require "db/validators/deploy_env/run_list" - +require "providers/static" module Devops module Model @@ -62,16 +62,9 @@ module Devops self end - def create provider, image, flavor, subnets, groups, out - res = {} - res[:before] = self.run_hook :before_create - return false unless provider.create_server(self, image, flavor, subnets, groups, out) - Devops::Db.connector.server_insert self - res[:after] = self.run_hook :after_create - res - end - def validate! + # remove it after updating from .types to validators + validate_fields_types super true end diff --git a/devops-service/db/mongo/models/stack/stack_base.rb b/devops-service/db/mongo/models/stack/stack_base.rb index 015ff94..cf118e5 100644 --- a/devops-service/db/mongo/models/stack/stack_base.rb +++ b/devops-service/db/mongo/models/stack/stack_base.rb @@ -19,6 +19,38 @@ module Devops run_list: {type: Array, value_type: String, empty: true, nil: true} # details: {type: Hash, nil: true} # Hash type isn't supported yet + set_field_validators :id, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :provider, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :project, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :deploy_env, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :stack_template, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :name, [::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :owner, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :run_list, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::Array, + ::Validators::FieldValidator::RunList] + def initialize attrs={} # self.provider = self.class.provider self.set_provider(attrs) @@ -34,6 +66,7 @@ module Devops self end + # TODO: use string keys def to_hash_without_id { project: project, @@ -44,7 +77,8 @@ module Devops # details are required to proper status handling details: bson_safe_details, stack_status: stack_status, - owner: owner + owner: owner, + run_list: run_list }.merge(provider_hash) end diff --git a/devops-service/db/mongo/models/stack/stack_ec2.rb b/devops-service/db/mongo/models/stack/stack_ec2.rb index 184a7fd..19ca0e9 100644 --- a/devops-service/db/mongo/models/stack/stack_ec2.rb +++ b/devops-service/db/mongo/models/stack/stack_ec2.rb @@ -1,16 +1,19 @@ +require 'db/mongo/models/stack/stack_base' + module Devops module Model class StackEc2 < StackBase def initialize attr={} - self.provider = 'ec2' super(attr) + self.provider = 'ec2' end def stack_status self.details['StackStatus'] if self.details end + # creation date is instance of unsupported by mongo class, so stringify it def bson_safe_details return unless details result = details.dup diff --git a/devops-service/db/mongo/models/stack/stack_openstack.rb b/devops-service/db/mongo/models/stack/stack_openstack.rb index ff3b06a..6202d58 100644 --- a/devops-service/db/mongo/models/stack/stack_openstack.rb +++ b/devops-service/db/mongo/models/stack/stack_openstack.rb @@ -1,3 +1,4 @@ +require 'db/mongo/models/stack/stack_base' module Devops module Model class StackOpenstack < StackBase diff --git a/devops-service/db/mongo/models/stack_template/stack_template_base.rb b/devops-service/db/mongo/models/stack_template/stack_template_base.rb index cbee6a8..7487724 100644 --- a/devops-service/db/mongo/models/stack_template/stack_template_base.rb +++ b/devops-service/db/mongo/models/stack_template/stack_template_base.rb @@ -16,6 +16,23 @@ module Devops template_body: {type: String, empty: false}, owner: {type: String, empty: false} + set_field_validators :id, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty, + ::Validators::FieldValidator::Name] + + set_field_validators :provider, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :template_body, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :owner, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + set_validators ::Validators::StackTemplate::TemplateContent def initialize(attrs) diff --git a/devops-service/db/mongo/models/stack_template/stack_template_ec2.rb b/devops-service/db/mongo/models/stack_template/stack_template_ec2.rb index 7d12269..89f7a0d 100644 --- a/devops-service/db/mongo/models/stack_template/stack_template_ec2.rb +++ b/devops-service/db/mongo/models/stack_template/stack_template_ec2.rb @@ -5,7 +5,7 @@ module Devops # In Amazon Cloudformation the template file must be stored on an Amazon S3 bucket. attr_accessor :template_url - types template_url: {type: String, empty: false} + # types template_url: {type: String, empty: false} def initialize(attrs) self.template_url = attrs['template_url'] diff --git a/devops-service/db/mongo/models/user.rb b/devops-service/db/mongo/models/user.rb index b16e032..c53a8dd 100644 --- a/devops-service/db/mongo/models/user.rb +++ b/devops-service/db/mongo/models/user.rb @@ -1,5 +1,6 @@ require "exceptions/invalid_record" require "exceptions/invalid_command" +require "exceptions/invalid_privileges" require "db/mongo/models/mongo_model" module Devops @@ -12,10 +13,7 @@ module Devops PRIVILEGES = ["r", "w", "x"] PRIVILEGES_REGEX = /^r?w?x?$/ - attr_accessor :id, :password, :privileges, :email - types :id => {:type => String, :empty => false}, - :email => {:type => String, :empty => false}, - :password => {:type => String, :empty => true} + KNOWN_ENTITIES = %w(flavor group image project server key user filter network provider script templates stack_template stack) set_field_validators :id, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, @@ -54,7 +52,7 @@ module Devops if !priv.empty? and PRIVILEGES_REGEX.match(priv).to_s.empty? raise InvalidCommand.new "Invalid privileges '#{priv}'. Available values are '#{PRIVILEGES.join("', '")}'" end - raise InvalidPrivileges.new "Can't grant privileges to root" if self.id == ROOT_USER_NAME + raise ::InvalidPrivileges.new "Can't grant privileges to root" if self.id == ROOT_USER_NAME case cmd when "all" @@ -114,22 +112,7 @@ module Devops def privileges_with_value value, options={} privileges = {} - [ - 'flavor', - 'group', - 'image', - 'project', - 'server', - 'key', - 'user', - 'filter', - 'network', - 'provider', - 'script', - 'templates', - 'stack_template', - 'stack' - ].each { |t| privileges.store(t, value) } + KNOWN_ENTITIES.each { |t| privileges.store(t, value) } privileges.merge(options) end diff --git a/devops-service/db/validators/base.rb b/devops-service/db/validators/base.rb index ff3245e..6e3b488 100644 --- a/devops-service/db/validators/base.rb +++ b/devops-service/db/validators/base.rb @@ -8,6 +8,8 @@ module Validators def validate! raise InvalidRecord.new(message) unless valid? + rescue StandardError => e + raise InvalidRecord.new("An error raised during validation with #{self.class}: #{e.class}: #{e.message}") end def valid? diff --git a/devops-service/db/validators/deploy_env/flavor.rb b/devops-service/db/validators/deploy_env/flavor.rb index 0a0be25..e0ff359 100644 --- a/devops-service/db/validators/deploy_env/flavor.rb +++ b/devops-service/db/validators/deploy_env/flavor.rb @@ -2,7 +2,8 @@ module Validators class DeployEnv::Flavor < Base def valid? - @model.provider_instance.flavors.detect do |flavor| + return true unless @model.flavor + available_flavors.detect do |flavor| flavor['id'] == @model.flavor end end @@ -10,5 +11,11 @@ module Validators def message "Invalid flavor '#{@model.flavor}'." end + + private + + def available_flavors + @model.provider_instance.flavors + end end end \ No newline at end of file diff --git a/devops-service/db/validators/deploy_env/groups.rb b/devops-service/db/validators/deploy_env/groups.rb index be4a3f7..43f7027 100644 --- a/devops-service/db/validators/deploy_env/groups.rb +++ b/devops-service/db/validators/deploy_env/groups.rb @@ -2,13 +2,20 @@ module Validators class DeployEnv::Groups < Base def valid? - subnets_filter = @model.send(:subnets_filter) - @invalid_groups = @model.groups - @model.provider_instance.groups(subnets_filter).keys + return true if @model.groups.nil? + @invalid_groups = @model.groups - available_groups @invalid_groups.empty? end def message "Invalid groups '#{@invalid_groups.join("', '")}'." end + + private + + def available_groups + subnets_filter = @model.subnets_filter + @model.provider_instance.groups(subnets_filter).keys + end end end \ No newline at end of file diff --git a/devops-service/db/validators/deploy_env/image.rb b/devops-service/db/validators/deploy_env/image.rb index b95cc67..6056d88 100644 --- a/devops-service/db/validators/deploy_env/image.rb +++ b/devops-service/db/validators/deploy_env/image.rb @@ -5,8 +5,8 @@ module Validators include ::ImageCommands def valid? - images = get_available_provider_images(::Devops::Db.connector, @model.provider) - images.detect do |image| + return true unless @model.image + available_images.detect do |image| image["id"] == @model.image end end @@ -14,5 +14,11 @@ module Validators def message "Invalid image '#{@model.image}'." end + + private + + def available_images + get_available_provider_images(::Devops::Db.connector, @model.provider) + end end end diff --git a/devops-service/db/validators/deploy_env/stack_template.rb b/devops-service/db/validators/deploy_env/stack_template.rb index dfbe69b..9b20d6d 100644 --- a/devops-service/db/validators/deploy_env/stack_template.rb +++ b/devops-service/db/validators/deploy_env/stack_template.rb @@ -2,20 +2,23 @@ module Validators class DeployEnv::StackTemplate < Base def valid? - if @model.stack_template - begin - Devops::Db.connector.stack_template(@model.stack_template) - true - rescue RecordNotFound => e - false - end - else - true + return true unless @model.stack_template + + available_stack_templates.detect do |template| + template['id'] == @model.stack_template end end def message "Invalid stack template '#{@model.stack_template}'." end + + private + + def available_stack_templates + # map to hash to simplify mocks. Later replace this method with something more suitable + Devops::Db.connector.stack_templates.map(&:to_hash) + end + end end diff --git a/devops-service/db/validators/field_validators/flavor.rb b/devops-service/db/validators/field_validators/flavor.rb index a06d104..4f86df4 100644 --- a/devops-service/db/validators/field_validators/flavor.rb +++ b/devops-service/db/validators/field_validators/flavor.rb @@ -4,7 +4,7 @@ module Validators class Flavor < Base def valid? - @model.provider_instance.flavors.detect do |flavor| + available_flavors.detect do |flavor| flavor['id'] == @value end end @@ -12,6 +12,12 @@ module Validators def message "Invalid flavor '#{@value}'." end + + private + + def available_flavors + @model.provider_instance.flavors + end end end end diff --git a/devops-service/db/validators/field_validators/image.rb b/devops-service/db/validators/field_validators/image.rb index 275f1b8..3dd727b 100644 --- a/devops-service/db/validators/field_validators/image.rb +++ b/devops-service/db/validators/field_validators/image.rb @@ -7,8 +7,7 @@ module Validators include ::ImageCommands def valid? - images = get_available_provider_images(::Devops::Db.connector, @model.provider) - images.detect do |image| + available_images.detect do |image| image["id"] == @value end end @@ -16,6 +15,12 @@ module Validators def message "Invalid image '#{@value}'." end + + private + + def available_images + get_available_provider_images(::Devops::Db.connector, @model.provider) + end end end end diff --git a/devops-service/db/validators/helpers/users.rb b/devops-service/db/validators/helpers/users.rb index 586bfe0..7b534bf 100644 --- a/devops-service/db/validators/helpers/users.rb +++ b/devops-service/db/validators/helpers/users.rb @@ -2,13 +2,18 @@ module Validators class Helpers::Users < Base def valid? - mongo_users = ::Devops::Db.connector.users_names(@model) - @nonexistent_users = @model - mongo_users + @nonexistent_users = (@model || []) - available_users @nonexistent_users.empty? end def message Devops::Messages.t("project.deploy_env.validation.users.not_exist", users: @nonexistent_users.join("', '")) end + + private + + def available_users + ::Devops::Db.connector.users_names(@model) + end end end diff --git a/devops-service/db/validators/image/bootstrap_template.rb b/devops-service/db/validators/image/bootstrap_template.rb index b1d53dc..2ced224 100644 --- a/devops-service/db/validators/image/bootstrap_template.rb +++ b/devops-service/db/validators/image/bootstrap_template.rb @@ -9,8 +9,7 @@ module Validators def valid? if @model.bootstrap_template - templates = get_templates - templates.include?(@model.bootstrap_template) + available_templates.include?(@model.bootstrap_template) else true end @@ -19,6 +18,12 @@ module Validators def message "Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'" end + + private + + def available_templates + get_templates + end end end end diff --git a/devops-service/db/validators/image/image_in_filter.rb b/devops-service/db/validators/image/image_in_filter.rb index 5bb2703..b85a09d 100644 --- a/devops-service/db/validators/image/image_in_filter.rb +++ b/devops-service/db/validators/image/image_in_filter.rb @@ -5,12 +5,17 @@ module Validators include ImageCommands def valid? - images = get_available_provider_images(::Devops::Db.connector, @model.provider) - images.map{|i| i["id"]}.include?(@model.id) + available_images.map{|i| i["id"]}.include?(@model.id) end def message "Invalid image id '#{@model.id}' for provider '#{@model.provider}', please check image filters" end + + private + + def available_images + get_available_provider_images(::Devops::Db.connector, @model.provider) + end end end diff --git a/devops-service/spec/factories/deploy_env.rb b/devops-service/spec/factories/deploy_env.rb new file mode 100644 index 0000000..9006524 --- /dev/null +++ b/devops-service/spec/factories/deploy_env.rb @@ -0,0 +1,28 @@ +require 'db/mongo/models/deploy_env/deploy_env_ec2' +require 'db/mongo/models/deploy_env/deploy_env_openstack' +require 'db/mongo/models/deploy_env/deploy_env_static' + +FactoryGirl.define do + factory :deploy_env, class: Devops::Model::DeployEnvBase do + identifier 'name' + run_list [] + users ['root'] + + factory :deploy_env_static, class: Devops::Model::DeployEnvStatic do + provider 'static' + end + + factory :cloud_deploy_env, class: Devops::Model::CloudDeployEnv do + flavor 'flavor' + image 'image' + + factory :deploy_env_ec2, class: Devops::Model::DeployEnvEc2 do + provider 'ec2' + end + + factory :deploy_env_openstack, class: Devops::Model::DeployEnvOpenstack do + provider 'openstack' + end + end + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/image.rb b/devops-service/spec/factories/image.rb new file mode 100644 index 0000000..e747460 --- /dev/null +++ b/devops-service/spec/factories/image.rb @@ -0,0 +1,10 @@ +require 'db/mongo/models/image' + +FactoryGirl.define do + factory :image, class: Devops::Model::Image do + id 'test_image' + remote_user 'root' + name 'my_image' + provider 'ec2' + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/key.rb b/devops-service/spec/factories/key.rb new file mode 100644 index 0000000..48b0262 --- /dev/null +++ b/devops-service/spec/factories/key.rb @@ -0,0 +1,8 @@ +require 'db/mongo/models/key' + +FactoryGirl.define do + factory :key, class: Devops::Model::Key do + path SpecSupport::BLANK_FILE + scope 'user' + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/report.rb b/devops-service/spec/factories/report.rb new file mode 100644 index 0000000..70395a5 --- /dev/null +++ b/devops-service/spec/factories/report.rb @@ -0,0 +1,20 @@ +require 'db/mongo/models/report' + +FactoryGirl.define do + factory :report, class: Devops::Model::Report do + + id 'id' + file 'path' + created_by 'root' + project 'project' + deploy_env 'env' + type 1 + created_at Time.now + updated_at Time.now + status 'completed' + + initialize_with { + new(attributes.stringify_keys) + } + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/server.rb b/devops-service/spec/factories/server.rb new file mode 100644 index 0000000..2878ec8 --- /dev/null +++ b/devops-service/spec/factories/server.rb @@ -0,0 +1,19 @@ +require 'db/mongo/models/server' + +FactoryGirl.define do + factory :server, class: Devops::Model::Server do + id 'server2' + provider 'ec2' + remote_user 'root' + project 'project' + deploy_env 'env' + private_ip '192.168.0.1' + key 'key_id' + created_by 'root' + chef_node_name 'chef_node_name' + reserved_by 'root' + run_list [] + last_operation_type 'creation' + last_operation_at Time.now + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/stack.rb b/devops-service/spec/factories/stack.rb new file mode 100644 index 0000000..fd58a82 --- /dev/null +++ b/devops-service/spec/factories/stack.rb @@ -0,0 +1,24 @@ +require 'db/mongo/models/stack/stack_ec2' +require 'db/mongo/models/stack/stack_openstack' + +FactoryGirl.define do + factory :stack, class: Devops::Model::StackBase do + id 'name' + project 'project' + deploy_env 'deploy_env' + stack_template 'template_id' + name 'iamstack' + owner 'root' + run_list [] + + initialize_with { new(attributes.stringify_keys) } + + factory :stack_ec2, class: Devops::Model::StackEc2 do + provider 'ec2' + end + + factory :stack_openstack, class: Devops::Model::StackOpenstack do + provider 'openstack' + end + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/stack_template.rb b/devops-service/spec/factories/stack_template.rb new file mode 100644 index 0000000..5e3ca24 --- /dev/null +++ b/devops-service/spec/factories/stack_template.rb @@ -0,0 +1,20 @@ +require 'db/mongo/models/stack_template/stack_template_ec2' +require 'db/mongo/models/stack_template/stack_template_openstack' + +FactoryGirl.define do + factory :stack_template, class: Devops::Model::StackTemplateBase do + id 'name' + template_body '{}' + owner 'root' + + initialize_with { new(attributes.stringify_keys) } + + factory :stack_template_ec2, class: Devops::Model::StackTemplateEc2 do + provider 'ec2' + end + + factory :stack_template_openstack, class: Devops::Model::StackTemplateOpenstack do + provider 'openstack' + end + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/user.rb b/devops-service/spec/factories/user.rb new file mode 100644 index 0000000..1df9840 --- /dev/null +++ b/devops-service/spec/factories/user.rb @@ -0,0 +1,9 @@ +require 'db/mongo/models/user' + +FactoryGirl.define do + factory :user, class: Devops::Model::User do + id 'john' + password '123456' + email 'test@google.com' + end +end \ No newline at end of file diff --git a/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb b/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb new file mode 100644 index 0000000..139397a --- /dev/null +++ b/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb @@ -0,0 +1,66 @@ +require 'db/mongo/models/deploy_env/deploy_env_ec2' + +RSpec.describe Devops::Model::DeployEnvEc2, type: :model do + let(:env) { build(:deploy_env_ec2) } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(ec2)) + allow_any_instance_of(Validators::Helpers::Users).to receive(:available_users).and_return(['root']) + allow_any_instance_of(Validators::DeployEnv::Flavor).to receive(:available_flavors).and_return([{'id' => 'flavor'}]) + allow_any_instance_of(Validators::FieldValidator::Flavor).to receive(:available_flavors).and_return([{'id' => 'flavor'}]) + allow_any_instance_of(Validators::DeployEnv::Groups).to receive(:available_groups).and_return(['default']) + allow_any_instance_of(Validators::DeployEnv::Image).to receive(:available_images).and_return([{'id' => 'image'}]) + allow_any_instance_of(Validators::DeployEnv::Image).to receive(:available_images).and_return([{'id' => 'image'}]) + allow_any_instance_of(Validators::DeployEnv::StackTemplate).to receive(:available_stack_templates).and_return([{'id' => 'template'}]) + allow_any_instance_of(Validators::FieldValidator::Image).to receive(:available_images).and_return([{'id' => 'image'}]) + end + + it_behaves_like 'deploy env' + it_behaves_like 'cloud deploy env' + + describe '#initialize' do + it 'keep only first subnet in given array' do + env = described_class.new('subnets' => %w(foo bar)) + expect(env.subnets).to match_array(%w(foo)) + end + end + + describe '#to_hash' do + it 'includes vpc_id' do + expect(env.to_hash).to include('vpc_id') + end + end + + describe '.create' do + it 'returns instance of DeployEnvEc2' do + expect(described_class.create({})).to be_an_instance_of(described_class) + end + end + + describe '#subnets_filter' do + before do + allow_any_instance_of(described_class).to receive_message_chain('provider_instance.networks') { + [{'name' => 'foo', 'vpcId' => 'bar'}] + } + end + + it 'returns nil if #subnets are empty' do + expect(build(:deploy_env_ec2, subnets: []).subnets_filter).to be_nil + end + + context "when provider has env's network" do + it 'returns hash with vpcId of that network' do + env = build(:deploy_env_ec2, subnets: ['foo']) + expect(env.subnets_filter).to eq({'vpc-id' => 'bar'}) + end + end + + context "when provider hasn't env's network" do + it 'returns nil' do + env = build(:deploy_env_ec2, subnets: ['wrong']) + expect(env.subnets_filter).to be nil + end + end + end + +end \ No newline at end of file diff --git a/devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb b/devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb new file mode 100644 index 0000000..428241f --- /dev/null +++ b/devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb @@ -0,0 +1,32 @@ +require 'db/mongo/models/deploy_env/deploy_env_openstack' + +RSpec.describe Devops::Model::DeployEnvOpenstack, type: :model do + let(:env) { build(:deploy_env_openstack) } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(openstack)) + allow_any_instance_of(Validators::Helpers::Users).to receive(:available_users).and_return(['root']) + allow_any_instance_of(Validators::DeployEnv::Flavor).to receive(:available_flavors).and_return([{'id' => 'flavor'}]) + allow_any_instance_of(Validators::FieldValidator::Flavor).to receive(:available_flavors).and_return([{'id' => 'flavor'}]) + allow_any_instance_of(Validators::DeployEnv::Groups).to receive(:available_groups).and_return(['default']) + allow_any_instance_of(Validators::DeployEnv::Image).to receive(:available_images).and_return([{'id' => 'image'}]) + allow_any_instance_of(Validators::DeployEnv::Image).to receive(:available_images).and_return([{'id' => 'image'}]) + allow_any_instance_of(Validators::DeployEnv::StackTemplate).to receive(:available_stack_templates).and_return([{'id' => 'template'}]) + allow_any_instance_of(Validators::FieldValidator::Image).to receive(:available_images).and_return([{'id' => 'image'}]) + end + + it_behaves_like 'deploy env' + it_behaves_like 'cloud deploy env' + + describe '.create' do + it 'returns instance of DeployEnvOpenstack' do + expect(described_class.create({})).to be_an_instance_of(described_class) + end + end + + describe '#subnets_filter' do + it 'always returns nil' do + expect(env.subnets_filter).to be nil + end + end +end \ No newline at end of file diff --git a/devops-service/spec/models/deploy_env/deploy_env_static_spec.rb b/devops-service/spec/models/deploy_env/deploy_env_static_spec.rb new file mode 100644 index 0000000..2f1d584 --- /dev/null +++ b/devops-service/spec/models/deploy_env/deploy_env_static_spec.rb @@ -0,0 +1,19 @@ +require 'db/mongo/models/deploy_env/deploy_env_static' + +RSpec.describe Devops::Model::DeployEnvStatic, type: :model do + let(:env) { build(:deploy_env_static) } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(static)) + allow_any_instance_of(Validators::Helpers::Users).to receive(:available_users).and_return(['root']) + end + + it_behaves_like 'deploy env' + + describe '.create' do + it 'returns instance of DeployEnvStatic' do + expect(described_class.create({})).to be_an_instance_of(Devops::Model::DeployEnvStatic) + end + end + +end \ No newline at end of file diff --git a/devops-service/spec/models/image_spec.rb b/devops-service/spec/models/image_spec.rb new file mode 100644 index 0000000..be7da26 --- /dev/null +++ b/devops-service/spec/models/image_spec.rb @@ -0,0 +1,47 @@ +require 'db/mongo/models/image' + +RSpec.describe Devops::Model::Image, type: :model do + let(:image) { build(:image) } + let(:name_with_dash) { 'asd-asd' } + let(:name_with_slash) { 'asd/asd' } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(openstack ec2 static')) + allow_any_instance_of(Validators::Image::ImageInFilter).to receive(:available_images).and_return([{'id' => 'test_image'}, {'id' => name_with_dash}, {'id' => name_with_slash}]) + end + + it 'is valid with correct attrs' do + expect(image).to be_valid + end + + describe 'validation' do + include_examples 'field type validation', :id, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :remote_user, :not_nil, :non_empty_string, :only_word_symbols, :field_validator + include_examples 'field type validation', :name, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :bootstrap_template, :maybe_nil, :non_empty_string, :only_word_symbols, :field_validator + + it 'id should contain only letters, digits and dashes' do + expect(build(:image, id: name_with_dash)).to be_valid + expect(build(:image, id: name_with_slash)).not_to be_valid + end + + it "id should be included in image filters" do + expect(build(:image, id: 'wrong')).not_to be_valid + end + + it 'name should contain only letters, digits and dashes' do + expect(build(:image, name: name_with_slash)).not_to be_valid + end + + it 'bootstrap_template should be included in available bootstrap templates' do + expect(build(:image, bootstrap_template: 'wrong')).not_to be_valid + end + end + + it '#to_hash_without_id returns provider, name, remote_user and bootstrap_template' do + expect(image.to_hash_without_id.keys).to match_array(%w(provider name remote_user bootstrap_template)) + end + + + +end \ No newline at end of file diff --git a/devops-service/spec/models/key_spec.rb b/devops-service/spec/models/key_spec.rb new file mode 100644 index 0000000..8a2108c --- /dev/null +++ b/devops-service/spec/models/key_spec.rb @@ -0,0 +1,28 @@ +require 'db/mongo/models/key' + +RSpec.describe Devops::Model::Key, type: :model do + let(:key) { build(:key) } + + it 'is valid with correct attrs' do + expect(key).to be_valid + end + + describe 'validations' do + it 'key file should exist in file system' do + expect(build(:key, path: './not_exist')).not_to be_valid + end + + it "scope could be 'user' or 'system'" do + expect(build(:key, scope: 'system')).to be_valid + expect(build(:key, scope: 'wrong')).not_to be_valid + end + end + + it '#filename returns base name of key file' do + expect(key.filename).to eq('blank_file') + end + + it '#to_hash_without_id returns path and scope' do + expect(key.to_hash_without_id.keys).to eq(%w(path scope)) + end +end \ No newline at end of file diff --git a/devops-service/spec/models/report_spec.rb b/devops-service/spec/models/report_spec.rb new file mode 100644 index 0000000..ab06af3 --- /dev/null +++ b/devops-service/spec/models/report_spec.rb @@ -0,0 +1,43 @@ +require 'db/mongo/models/report' + +RSpec.describe Devops::Model::Report, type: :model do + let(:report) { build(:report) } + + describe '#initialize' do + it 'converts created_at to localtime' do + now = Time.now.utc + expect(now.zone).to eq 'UTC' + expect( + build(:report, created_at: now).created_at.zone + ).not_to eq 'UTC' + end + + it 'converts updated_at to localtime' do + now = Time.now.utc + expect(now.zone).to eq 'UTC' + expect( + build(:report, updated_at: now).updated_at.zone + ).not_to eq 'UTC' + end + end + + describe '#to_hash_without_id' do + it 'contains several fields' do + expect(report.to_hash_without_id.keys).to match_array(%w( + file + created_at + updated_at + created_by + project + deploy_env + type + chef_node_name + host + status + stack + subreports + job_result_code + )) + end + end +end \ No newline at end of file diff --git a/devops-service/spec/models/server_spec.rb b/devops-service/spec/models/server_spec.rb new file mode 100644 index 0000000..b018979 --- /dev/null +++ b/devops-service/spec/models/server_spec.rb @@ -0,0 +1,95 @@ +require 'db/mongo/models/server' + +RSpec.describe Devops::Model::Server, type: :model do + let(:server) { build(:server) } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(openstack ec2 static')) + end + + it 'is valid with correct attrs' do + expect(server).to be_valid + end + + describe 'validation rules:' do + include_examples 'field type validation', :id, :not_nil, :non_empty_string + include_examples 'field type validation', :provider, :not_nil, :non_empty_string + include_examples 'field type validation', :remote_user, :not_nil, :non_empty_string + include_examples 'field type validation', :project, :not_nil, :non_empty_string + include_examples 'field type validation', :deploy_env, :not_nil, :non_empty_string + include_examples 'field type validation', :private_ip, :not_nil, :non_empty_string + include_examples 'field type validation', :public_ip, :maybe_nil, :maybe_empty_string + include_examples 'field type validation', :key, :not_nil, :non_empty_string + include_examples 'field type validation', :created_by, :not_nil, :non_empty_string + include_examples 'field type validation', :chef_node_name, :not_nil, :maybe_empty_string + include_examples 'field type validation', :reserved_by, :not_nil, :maybe_empty_string + include_examples 'field type validation', :stack, :maybe_nil, :non_empty_string + include_examples 'field type validation', :run_list, :not_nil, :maybe_empty_array + include_examples 'field type validation', :run_list, :run_list + end + + describe '#initialize' do + it 'sets run_list to empty array if it is nil' do + server = described_class.new() + expect(server.run_list).to eq [] + end + end + + describe '.fields' do + it 'is array with several strings' do + expect(described_class.fields).to match_array(%w( + chef_node_name project deploy_env provider remote_user + private_ip public_ip created_at created_by key reserved_by + run_list stack last_operation_type last_operation_at + )) + end + end + + it '#to_hash_without_id returns not nil fields' do + server = described_class.new('run_list' => [], 'project' => 'asd') + expect(server.to_hash_without_id.keys).to match_array(%w(run_list project)) + server.stack = 'stack_id' + expect(server.to_hash_without_id.keys).to match_array(%w(run_list project stack)) + end + + describe '#info' do + it 'returns string' do + expect(server.info).to be_a(String) + end + end + + describe '#static?' do + it 'returns true for servers with provider==static' do + expect(build(:server, provider: 'static').static?).to be true + end + + it 'returns false for servers with provider==ec2' do + expect(server.static?).to be false + end + end + + describe '#set_last_operation' do + it 'sets #last_operation_at to Time.now' do + prev_operation_time = server.last_operation_at + server.set_last_operation('deploy') + expect(server.last_operation_at).to be > prev_operation_time + end + + it 'updates last_operation_type' do + server.set_last_operation('deploy') + expect(server.last_operation_type).to eq 'deploy' + end + + it 'it raises error if given type is not supported' do + expect { + server.set_last_operation(:wrong) + }.to raise_error ArgumentError + end + + it 'expects string as an argument' do + expect { + server.set_last_operation(:deploy) + }.to raise_error ArgumentError + end + end +end diff --git a/devops-service/spec/models/stack/stack_ec2_spec.rb b/devops-service/spec/models/stack/stack_ec2_spec.rb new file mode 100644 index 0000000..17fa499 --- /dev/null +++ b/devops-service/spec/models/stack/stack_ec2_spec.rb @@ -0,0 +1,174 @@ +require 'db/mongo/models/stack/stack_ec2' + +RSpec.describe Devops::Model::StackEc2, type: :model do + let(:stack) { build(:stack_ec2) } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(ec2)) + end + + it 'is valid with factory attrs' do + expect(build(:stack_ec2)).to be_valid + end + + describe 'validation rules:' do + include_examples 'field type validation', :id, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :project, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :deploy_env, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :stack_template, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :name, :maybe_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :owner, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :run_list, :maybe_nil, :maybe_empty_array, :run_list, :field_validator + + it 'validates provider' do + stack.provider = nil + expect(stack).not_to be_valid + + stack.provider = '' + expect(stack).not_to be_valid + end + end + + describe '#to_hash_without_id' do + it 'returns hash with several fields' do + expect(stack.to_hash_without_id.keys).to include('provider', :project, :deploy_env, :stack_template, :name, :owner, :run_list) + end + end + + describe '#initialize' do + it 'sets provider to ec2 even if given attrs don\'t contain it' do + expect(described_class.new.provider).to eq 'ec2' + end + + it 'sets run_list to empty array if it\'s blank' do + expect(described_class.new.run_list).to eq [] + end + end + + describe '#bson_safe_details' do + it 'stringify value of CreationTime key' do + stack.details = {'CreationTime' => Time.now} + expect(stack.bson_safe_details['CreationTime']).to be_a(String) + end + end + + describe '#create_stack_in_cloud!' do + it 'calls create_stack method of provider instance' do + expect(stack).to receive_message_chain('provider_instance.create_stack') + stack.create_stack_in_cloud!('') + end + + it 'raises InvalidRecord if stack with such name already exists in cloud' do + allow(stack).to receive_message_chain('provider_instance.create_stack').and_raise(ProviderErrors::NameConflict) + expect { stack.create_stack_in_cloud!('') }.to raise_error(InvalidRecord) + end + end + + describe '#delete_stack_in_cloud!' do + it 'calls delete_stack method of provider instance' do + expect(stack).to receive_message_chain('provider_instance.delete_stack').with(stack) + stack.delete_stack_in_cloud! + end + end + + describe '#template_body' do + it 'is a String' do + allow(Devops::Db).to receive_message_chain('connector.stack_template.template_body') {'body'} + expect(stack.template_body).to be_a(String) + end + end + + + describe '#sync_details!' do + let(:stack_with_blank_info) { stack.details = nil; stack.events=nil; stack } + let(:fresh_details) { {'StackStatus' => 'CREATE_COMPLETE'} } + let(:fresh_events) { [] } + subject { stack_with_blank_info.sync_details! } + + before do + allow(stack_with_blank_info).to receive_message_chain('provider_instance.stack_details') {fresh_details} + allow(stack_with_blank_info).to receive_message_chain('provider_instance.stack_events') {fresh_events} + end + + it "get fresh stack details and stores it in @details" do + expect(stack_with_blank_info).to receive_message_chain('provider_instance.stack_details') + subject + expect(stack_with_blank_info.details).to eq fresh_details + end + + it "get fresh stack events and stores it in @events" do + expect(stack_with_blank_info).to receive_message_chain('provider_instance.stack_events') + subject + expect(stack_with_blank_info.events).to eq fresh_events + end + + it 'updates stack status' do + subject + expect(stack_with_blank_info.stack_status).to eq 'CREATE_COMPLETE' + end + end + + describe '#update_in_cloud!' do + before { allow(stack).to receive_message_chain('provider_instance.update_stack') } + + it 'calls to method update_stack of provider instance' do + expect(stack).to receive_message_chain('provider_instance.update_stack') + stack.update_in_cloud!({}) + end + + it 'filters params to include only Parameters, TemplateBody, TemplateURL and Capabilities' do + permitted_keys = %w(Parameters TemplateBody TemplateURL Capabilities) + permitted_hash = permitted_keys.inject({}) {|hash, key| hash[key] = 1; hash} + unfiltered_hash = permitted_hash.merge('wrong' => 1) + expect(stack).to receive_message_chain('provider_instance.update_stack').with(stack, permitted_hash) + stack.update_in_cloud!(unfiltered_hash) + end + end + + describe '#change_stack_template!' do + let(:new_template) { 'brand_new_template' } + before do + allow(stack).to receive(:template_body) + allow(stack).to receive(:update_in_cloud!) + allow(Devops::Db).to receive_message_chain('connector.stack_update') + end + + it 'updates stack_template' do + stack.change_stack_template!(new_template) + expect(stack.stack_template).to eq new_template + end + end + + describe '.create' do + subject { described_class.create({}, '') } + + before do + allow_any_instance_of(described_class).to receive(:create_stack_in_cloud!) + allow_any_instance_of(described_class).to receive(:sync_details!) + end + + it "returns instance of #{described_class.name}" do + expect(subject).to be_a(described_class) + end + + it 'creates stack in AWS' do + expect_any_instance_of(described_class).to receive(:create_stack_in_cloud!) + subject + end + + it 'synchronizes details' do + expect_any_instance_of(described_class).to receive(:sync_details!) + subject + end + end + + describe '.build_from_bson' do + subject { described_class.build_from_bson('_id' => 'foo', 'name' => 'bar') } + it "returns instance of #{described_class.name} with given parameters" do + expect(subject.id).to eq 'foo' + expect(subject.name).to eq 'bar' + end + end + +end + diff --git a/devops-service/spec/models/stack_template/stack_template_ec2_spec.rb b/devops-service/spec/models/stack_template/stack_template_ec2_spec.rb new file mode 100644 index 0000000..1cb48af --- /dev/null +++ b/devops-service/spec/models/stack_template/stack_template_ec2_spec.rb @@ -0,0 +1,13 @@ +require 'db/mongo/models/stack_template/stack_template_ec2' + +RSpec.describe Devops::Model::StackTemplateEc2, type: :model do + let(:stack_template) { build(:stack_template_ec2) } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(ec2)) + allow_any_instance_of(Devops::Model::StackTemplateEc2).to receive_message_chain('provider_instance.validate_stack_template') { true } + end + + it_behaves_like 'stack template' + +end \ No newline at end of file diff --git a/devops-service/spec/models/stack_template/stack_template_openstack_spec.rb b/devops-service/spec/models/stack_template/stack_template_openstack_spec.rb new file mode 100644 index 0000000..fa578eb --- /dev/null +++ b/devops-service/spec/models/stack_template/stack_template_openstack_spec.rb @@ -0,0 +1,13 @@ +require 'db/mongo/models/stack_template/stack_template_openstack' + +RSpec.describe Devops::Model::StackTemplateOpenstack, type: :model do + let(:stack_template) { build(:stack_template_openstack) } + + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(openstack)) + allow_any_instance_of(Devops::Model::StackTemplateOpenstack).to receive_message_chain('provider_instance.validate_stack_template') { true } + end + + it_behaves_like 'stack template' + +end \ No newline at end of file diff --git a/devops-service/spec/models/user_spec.rb b/devops-service/spec/models/user_spec.rb new file mode 100644 index 0000000..c060ff4 --- /dev/null +++ b/devops-service/spec/models/user_spec.rb @@ -0,0 +1,107 @@ +require 'db/mongo/models/user' + +RSpec.describe Devops::Model::User, type: :model do + let(:user) { build(:user) } + + it 'is valid with correct attrs' do + expect(user).to be_valid + end + + describe 'validation' do + include_examples 'field type validation', :id, :not_nil, :non_empty_string, :only_word_symbols, :field_validator + include_examples 'field type validation', :password, :not_nil, :maybe_empty_string, :field_validator + include_examples 'field type validation', :email, :not_nil, :maybe_empty_string, :field_validator + + it "privileges is set to default value if nil is passed" do + expect(user.privileges).not_to be nil + end + + it 'privileges should be a hash' do + expect(build(:user, privileges: 'root')).not_to be_valid + end + end + + it '#to_hash_without_id returns email, password and privileges' do + expect(user.to_hash_without_id.keys).to match_array(%w(email password privileges)) + end + + describe '#privileges' do + subject {user.privileges} + it { should be_a(Hash) } + + it 'has entity types as keys' do + expect(subject.keys).to match_array(Devops::Model::User::KNOWN_ENTITIES) + end + end + + describe '#all_privileges' do + it 'all privileges are set to rwx' do + expect(user.all_privileges.values.uniq).to eq ['rwx'] + end + end + + describe '#default_privileges' do + it 'user privilege is empty' do + expect(user.default_privileges['user']).to be_empty + end + + it 'others are r' do + result = user.default_privileges + result.delete('user') + expect(result.values.uniq).to eq ['r'] + end + end + + describe '#grant' do + it 'sets privilege' do + expect { + user.grant('key', 'rw') + }.to change{ user.privileges['key'] }.from('r').to('rw') + end + it 'could not be applied to root' do + root = build(:user, id: 'root') + expect{root.grant('user', 'r')}.to raise_error(InvalidPrivileges) + end + + it 'recognize all value of entity' do + user.grant('all', 'rw') + expect(user.privileges.values.uniq).to eq ['rw'] + end + + it 'sets default privileges if cmd is empty' do + user.grant('user', 'rw') + user.grant('', 'rwx') + expect(user.privileges).to eq user.default_privileges + end + + it 'could not be applied with unknown privilege value' do + expect{user.grant('user', 'q')}.to raise_error(InvalidCommand) + end + end + + describe '#check_privileges' do + it "raises InvalidPrivileges if user hasn't specified privilege" do + expect { user.check_privileges('key', 'w') }.to raise_error(InvalidPrivileges) + end + + it 'does nothing is user has specified privilege' do + user.check_privileges('key', 'r') + end + end + + describe '.create_root' do + subject {Devops::Model::User.create_root} + it { should be_valid } + it 'his name is root' do + expect(subject.id).to eq Devops::Model::User::ROOT_USER_NAME + end + + it 'his password is root password' do + expect(subject.password).to eq Devops::Model::User::ROOT_PASSWORD + end + + it 'has all privileges' do + expect(subject.privileges).to eq user.all_privileges + end + end +end diff --git a/devops-service/spec/spec_helper.rb b/devops-service/spec/spec_helper.rb new file mode 100644 index 0000000..7552bf9 --- /dev/null +++ b/devops-service/spec/spec_helper.rb @@ -0,0 +1,78 @@ +ENV['RACK_ENV'] ||= 'test' +require 'byebug' +require 'factory_girl' +require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/inflector' + +# setup load_path and require support files +root = File.join(File.dirname(__FILE__), "..") +$LOAD_PATH.push root unless $LOAD_PATH.include? root + +Dir[("./spec/support/**/*.rb")].each { |f| require f } + +# Factory girl configuration +FactoryGirl.define do + # do not try to persist, but raise validation errors + to_create { |model| model.validate! } +end +FactoryGirl.find_definitions + +# RSpec configuration +RSpec.configure do |config| + config.include FactoryGirl::Syntax::Methods + + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + # config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +end \ No newline at end of file diff --git a/devops-service/spec/support/blank_file b/devops-service/spec/support/blank_file new file mode 100644 index 0000000..e69de29 diff --git a/devops-service/spec/support/shared_cloud_deploy_env_specs.rb b/devops-service/spec/support/shared_cloud_deploy_env_specs.rb new file mode 100644 index 0000000..5546022 --- /dev/null +++ b/devops-service/spec/support/shared_cloud_deploy_env_specs.rb @@ -0,0 +1,38 @@ +RSpec.shared_examples 'cloud deploy env' do + validated_model_name = described_class.name.demodulize.underscore + + describe 'validation rules:' do + include_examples 'field type validation', :flavor, :maybe_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :image, :maybe_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :groups, :maybe_nil, :maybe_empty_array, :field_validator + include_examples 'field type validation', :subnets, :maybe_nil, :maybe_empty_array, :field_validator + include_examples 'field type validation', :stack_template, :maybe_nil, :non_empty_string, :field_validator + + it 'flavor should be available' do + expect(build(validated_model_name, flavor: 'flavor')).to be_valid + expect(build(validated_model_name, flavor: 'wrong')).not_to be_valid + end + + it 'image should be available' do + expect(build(validated_model_name, image: 'image')).to be_valid + expect(build(validated_model_name, image: 'wrong')).not_to be_valid + end + + it 'all groups should be available' do + expect(build(validated_model_name, groups: ['default'])).to be_valid + expect(build(validated_model_name, groups: ['wrong'])).not_to be_valid + expect(build(validated_model_name, groups: ['wrong', 'default'])).not_to be_valid + end + + it 'stack_template should be available' do + expect(build(validated_model_name, stack_template: 'template')).to be_valid + expect(build(validated_model_name, stack_template: 'wrong')).not_to be_valid + end + end + + describe '#to_hash' do + it 'includes flavor, image, subnets, groups and stack_template in keys' do + expect(described_class.new.to_hash).to include('flavor', 'image', 'subnets', 'groups', 'stack_template') + end + end +end \ No newline at end of file diff --git a/devops-service/spec/support/shared_deploy_env_specs.rb b/devops-service/spec/support/shared_deploy_env_specs.rb new file mode 100644 index 0000000..de65ed3 --- /dev/null +++ b/devops-service/spec/support/shared_deploy_env_specs.rb @@ -0,0 +1,211 @@ +require 'core/devops-application' +require 'core/devops-db' + +RSpec.shared_examples 'deploy env' do + validated_model_name = described_class.name.demodulize.underscore + include SpecSupport + + it 'is valid with correct attrs' do + expect(env).to be_valid + end + + describe 'validation rules:' do + include_examples 'field type validation', :identifier, :not_nil, :non_empty_string, :only_word_symbols, :field_validator + include_examples 'field type validation', :run_list, :not_nil, :maybe_empty_array, :run_list, :field_validator + include_examples 'field type validation', :users, :not_nil, :maybe_empty_array, :field_validator + include_examples 'field type validation', :expires, :maybe_nil, :non_empty_string, :field_validator + + it 'should be valid only with all users available' do + expect(build(validated_model_name, users: ['root'])).to be_valid + expect(build(validated_model_name, users: ['wrong'])).not_to be_valid + expect(build(validated_model_name, users: ['wrong', 'root'])).not_to be_valid + end + + it 'expires should be smth like "2d" or "15h"' do + expect(build(validated_model_name, expires: '2d')).to be_valid + expect(build(validated_model_name, expires: 'wrong')).not_to be_valid + end + + describe '#validate_provider!' do + it 'raises InvalidRecord if provider is nil' do + env.provider = nil + expect {env.validate_provider!}.to raise_error InvalidRecord + end + end + end + + describe '#initialize' do + it 'sets identifier from given hash' do + model = described_class.new('identifier' => 'a') + expect(model.identifier).to eq 'a' + end + it 'sets provider from given hash' do + model = described_class.new('provider' => 'ec2') + expect(model.provider).to eq 'ec2' + end + it 'sets uniq elements from run list from given hash' do + model = described_class.new('run_list' => ['role[a]', 'role[a]', 'recipe[e]']) + expect(model.run_list).to match_array ['role[a]', 'recipe[e]'] + end + it 'sets empty array if run list is empty' do + model = described_class.new + expect(model.run_list).to match_array [] + end + it 'sets expires from given hash' do + model = described_class.new('expires' => '3d') + expect(model.expires).to eq '3d' + end + it 'sets uniq users from given hash' do + model = described_class.new('users' => ['root', 'root', 'user']) + expect(model.users).to match_array ['root', 'user'] + end + end + + describe '#to_hash' do + it 'includes identifier, run_list, expires, users and provider in keys' do + expect(described_class.new.to_hash).to include('identifier', 'run_list', 'expires', 'users', 'provider') + end + end + + describe '#add_users' do + it "doesn't add duplications of already added users" do + env = described_class.new('users' => %w(root user)) + env.add_users %w(root user) + expect(env.users).to match_array %w(root user) + end + + it "adds new users" do + env = described_class.new('users' => %w(root)) + env.add_users ['user1', 'user2'] + expect(env.users).to match_array %w(root user1 user2) + end + end + + describe '#update_field' do + subject { env.update_field('project_name', 'run_list', ['role[asd]']) } + before do + allow_message_expectations_on_nil + allow(Devops::Db.connector).to receive(:set_project_deploy_env_field) + end + + it 'validate it' do + expect(env).to receive(:validate_run_list!) + subject + end + + it 'updates attribute value' do + expect { subject }.to change {env.run_list}.to(['role[asd]']) + end + + it 'saves it' do + expect(Devops::Db.connector).to receive(:set_project_deploy_env_field).with('project_name', env.identifier, {'run_list' => ['role[asd]']}) + subject + end + end + + describe '#create_role' do + subject { env.create_role('project_name') } + before do + stub_loggers + allow(env).to receive_message_chain('knife_instance.role_name') { 'role_name' } + end + + it 'returns nil if knife_instance is nil' do + allow(env).to receive(:knife_instance) + expect(subject).to be nil + end + + it 'returns hash with :error if knife.roles is nil' do + allow(env).to receive_message_chain('knife_instance.roles') + expect(subject).to be_a(Hash).and include(:error) + end + + it 'returns hash with :error when creating a role raises an error' do + allow(env).to receive_message_chain('knife_instance.roles') { [] } + allow(env).to receive_message_chain('knife_instance.create_role') { raise StandardError } + expect(subject).to be_a(Hash).and include(error: ['role_name']) + end + + context 'when a role already exists in chef' do + before { allow(env).to receive_message_chain('knife_instance.roles') { ['role_name'] } } + it 'returns hash with :exist' do + expect(subject).to be_a(Hash).and include(exist: ['role_name']) + end + + it "adds the role to env's run_list" do + subject + expect(env.run_list).to include('role[role_name]') + end + end + + context 'when a role is missing in chef' do + before { allow(env).to receive_message_chain('knife_instance.roles') { [] } } + it 'returns hash with :new when a role has been created' do + expect(env).to receive_message_chain('knife_instance.create_role') + expect(subject).to be_a(Hash).and include(new: ['role_name']) + end + + it "adds the role to env's run_list" do + allow(env).to receive_message_chain('knife_instance.create_role') + subject + expect(env.run_list).to include('role[role_name]') + end + end + end + + describe '#rename' do + subject { env.rename('project_id', 'new_name') } + let(:old_role_name) {'project_id_name'} + let(:new_role_name) {'project_id_new_name'} + let(:suggested_old_roles) {["role[#{old_role_name}]"]} + let(:suggested_new_roles) {["role[#{new_role_name}]"]} + + before do + stub_loggers + # simulate correct start conditions + env.run_list = suggested_old_roles + + # stub calls to knife + allow(env).to receive(:create_role) { {exist: [new_role_name]} } + allow(env).to receive_message_chain('knife_instance.role_name') { old_role_name } + + # there is a call to Project.create_roles_response, stub it + allow_message_expectations_on_nil + project_class_double = double() + allow(project_class_double).to receive(:create_roles_response) { ''} + stub_const('Devops::Model::Project', project_class_double) + end + + it 'raises InvalidRecord if env with such name already exists in project' do + allow(Devops::Db.connector).to receive_message_chain('project.deploy_env') + expect{subject}.to raise_error(InvalidRecord) + end + + context 'when there is no env with such name already' do + before do + allow(Devops::Db.connector).to receive_message_chain('project.deploy_env') {raise RecordNotFound} + allow(Devops::Db.connector).to receive(:set_project_deploy_env_field) + end + + it 'validates new identifier' do + expect(env).to receive(:validate_identifier!) + subject + end + + it 'removes old role and adds new one' do + # somewhy 'expect to change' did't work + expect(env.run_list).to match_array(suggested_old_roles) + subject + expect(env.run_list).to match_array(suggested_new_roles) + end + end + end + + describe '#knife_instance' do + it 'calls KnifeFactory.instance' do + expect(KnifeFactory).to receive(:instance) + env.knife_instance + end + end + +end \ No newline at end of file diff --git a/devops-service/spec/support/shared_stack_template_specs.rb b/devops-service/spec/support/shared_stack_template_specs.rb new file mode 100644 index 0000000..e8c1dc4 --- /dev/null +++ b/devops-service/spec/support/shared_stack_template_specs.rb @@ -0,0 +1,53 @@ +RSpec.shared_examples 'stack template' do + validated_model_name = described_class.name.demodulize.underscore + + it 'is valid with factory attrs' do + expect(build(validated_model_name)).to be_valid + end + + describe 'validation rules' do + include_examples 'field type validation', :id, :not_nil, :non_empty_string, :only_word_symbols, :field_validator + include_examples 'field type validation', :provider, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :template_body, :not_nil, :non_empty_string, :field_validator + include_examples 'field type validation', :owner, :not_nil, :non_empty_string, :field_validator + end + + describe '#to_hash_without_id' do + it 'returns hash with provider, template_body and owner' do + expect(stack_template.to_hash_without_id).to include(:provider, :template_body, :owner) + end + end + + describe '.create' do + it "returns instance of #{described_class.name}" do + params = { + 'id' => 'foo', + 'template_body' => '{}', + 'owner' => 'root', + 'provider' => 'ec2' + } + expect(described_class.create(params)).to be_an_instance_of(described_class) + end + end + + describe '#build_from_bson' do + let(:params) { { + '_id' => 'foo', + 'template_body' => '{}', + 'owner' => 'root', + 'provider' => 'ec2' + }} + + subject { described_class.build_from_bson(params) } + + it 'sets id from _id' do + expect(subject.id).to eq 'foo' + end + + it 'sets other params from given hash' do + expect(subject.template_body).to eq '{}' + expect(subject.owner).to eq 'root' + expect(subject.provider).to eq 'ec2' + end + end +end \ No newline at end of file diff --git a/devops-service/spec/support/shared_validation_specs.rb b/devops-service/spec/support/shared_validation_specs.rb new file mode 100644 index 0000000..487e8d7 --- /dev/null +++ b/devops-service/spec/support/shared_validation_specs.rb @@ -0,0 +1,121 @@ +SUPPORTED_MODEL_PROPERTIES = [:not_nil, :maybe_nil, :non_empty_string, :maybe_empty_string, :non_empty_array, :maybe_empty_array, :only_word_symbols, :run_list, :field_validator] + +RSpec.shared_examples 'field type validation' do |field, *properties| + validated_model_name = described_class.name.demodulize.underscore + unsupported_properties = properties - SUPPORTED_MODEL_PROPERTIES + with_field_validators = properties.include?(:field_validator) + + raise_error "Unsupported properties: #{unsupported_properties.join(', ')}" unless unsupported_properties.empty? + + describe field do + it 'should not be nil' do + expect(build(validated_model_name, field => nil)).not_to be_valid + end if properties.include?(:not_nil) + + it 'may be nil' do + expect(build(validated_model_name, field => nil)).to be_valid + end if properties.include?(:maybe_nil) + + it 'should be non empty string' do + expect(build(validated_model_name, field => 0)).not_to be_valid + expect(build(validated_model_name, field => '')).not_to be_valid + end if properties.include?(:non_empty_string) + + it 'may be empty string' do + expect(build(validated_model_name, field => 0)).not_to be_valid + expect(build(validated_model_name, field => '')).to be_valid + end if properties.include?(:maybe_empty_string) + + it 'should be non empty array' do + expect(build(validated_model_name, field => 0)).not_to be_valid + expect(build(validated_model_name, field => [])).not_to be_valid + end if properties.include?(:non_empty_array) + + it 'may be empty array' do + expect(build(validated_model_name, field => 0)).not_to be_valid + expect(build(validated_model_name, field => [])).to be_valid + end if properties.include?(:maybe_empty_array) + + it 'should contain only word symbols' do + expect(build(validated_model_name, field => 'asd-asd')).not_to be_valid + end if properties.include?(:only_word_symbols) + + it 'should contain elements like role[asd] or recipe[asd]' do + expect(build(validated_model_name, field => ['role[asd]'])).to be_valid + expect(build(validated_model_name, field => ['recipe[asd]'])).to be_valid + expect(build(validated_model_name, field => ['wrong_format'])).not_to be_valid + end if properties.include?(:run_list) + end + + if with_field_validators + field_validation_method = "validate_#{field}!" + + describe field_validation_method do + it 'should not be nil' do + expect{ + build(validated_model_name, field => nil).send(field_validation_method) + }.to raise_error InvalidRecord + end if properties.include?(:not_nil) + + it 'may be nil' do + expect{ + build(validated_model_name, field => nil).send(field_validation_method) + }.not_to raise_error + end if properties.include?(:maybe_nil) + + it 'should be non empty string' do + expect{ + build(validated_model_name, field => 0).send(field_validation_method) + }.to raise_error InvalidRecord + expect{ + build(validated_model_name, field => '').send(field_validation_method) + }.to raise_error InvalidRecord + end if properties.include?(:non_empty_string) + + it 'may be empty string' do + expect{ + build(validated_model_name, field => 0).send(field_validation_method) + }.to raise_error InvalidRecord + expect{ + build(validated_model_name, field => '').send(field_validation_method) + }.not_to raise_error + end if properties.include?(:maybe_empty_string) + + it 'should be non empty array' do + expect{ + build(validated_model_name, field => 0).send(field_validation_method) + }.to raise_error InvalidRecord + expect{ + build(validated_model_name, field => []).send(field_validation_method) + }.to raise_error InvalidRecord + end if properties.include?(:non_empty_array) + + it 'may be empty array' do + expect{ + build(validated_model_name, field => 0).send(field_validation_method) + }.to raise_error InvalidRecord + expect{ + build(validated_model_name, field => []).send(field_validation_method) + }.not_to raise_error + end if properties.include?(:maybe_empty_array) + + it 'should contain only word symbols' do + expect{ + build(validated_model_name, field => 'asd-asd').send(field_validation_method) + }.to raise_error InvalidRecord + end if properties.include?(:only_word_symbols) + + it 'should contain elements like role[asd] or recipe[asd]' do + expect{ + build(validated_model_name, field => ['role[asd]']).send(field_validation_method) + }.not_to raise_error + expect{ + build(validated_model_name, field => ['recipe[asd]']).send(field_validation_method) + }.not_to raise_error + expect{ + build(validated_model_name, field => ['wrong_format']).send(field_validation_method) + }.to raise_error InvalidRecord + end if properties.include?(:run_list) + end + end +end \ No newline at end of file diff --git a/devops-service/spec/support/spec_support.rb b/devops-service/spec/support/spec_support.rb new file mode 100644 index 0000000..159e1cf --- /dev/null +++ b/devops-service/spec/support/spec_support.rb @@ -0,0 +1,11 @@ +require 'core/devops-application' + +module SpecSupport + BLANK_FILE = File.join(Devops::Application.root, 'spec/support/blank_file') + + def stub_loggers + allow(DevopsLogger).to receive_message_chain('logger.debug') + allow(DevopsLogger).to receive_message_chain('logger.info') + allow(DevopsLogger).to receive_message_chain('logger.error') + end +end \ No newline at end of file From 39a8ff0eaebad44c05b7a4e4c34806ca0015e6c8 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 20 Nov 2015 15:15:06 +0300 Subject: [PATCH 16/18] Fix errors occured after cherry-picking squashed specs This reverts commit ee3b49bbea80db0a66eb3c8f116dcf100ff4a402. --- .../models/deploy_env/cloud_deploy_env.rb | 3 +- .../mongo/models/deploy_env/deploy_env_ec2.rb | 5 ++- devops-service/db/mongo/models/server.rb | 19 +++++++---- .../db/mongo/models/stack/stack_base.rb | 32 +++++++++---------- .../stack_template/stack_template_base.rb | 16 +++++----- devops-service/db/mongo/models/user.rb | 2 ++ .../models/deploy_env/deploy_env_ec2_spec.rb | 6 ---- 7 files changed, 44 insertions(+), 39 deletions(-) diff --git a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb index dc62b4d..628ca17 100644 --- a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb +++ b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb @@ -32,8 +32,7 @@ module Devops # ::Validators::FieldValidator::Groups.new set_field_validators :stack_template, ::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty, -# ::Validators::FieldValidator::StackTemplate.new + ::Validators::FieldValidator::NotEmpty def initialize d={} super(d) 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 22424e7..b6285bc 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 @@ -44,7 +44,10 @@ module Devops networks = provider_instance.networks unless self.subnets.empty? - {"vpc-id" => networks.detect{|n| n["name"] == self.subnets[0]}["vpcId"] } + network = networks.detect {|n| n["name"] == self.subnets[0]} + if network + {"vpc-id" => network["vpcId"] } + end end end diff --git a/devops-service/db/mongo/models/server.rb b/devops-service/db/mongo/models/server.rb index 6b4bcaf..aae8ecf 100644 --- a/devops-service/db/mongo/models/server.rb +++ b/devops-service/db/mongo/models/server.rb @@ -10,12 +10,18 @@ module Devops include ModelWithProvider module OperationType - CREATION = :creation - DEPLOY = :deploy - RESERVE = :reserve - UNRESERVE = :unreserve - PAUSE = :pause - UNPAUSE = :unpause + # we store strings in mongo, so it's better not to use symbols + CREATION = 'creation' + DEPLOY = 'deploy' + RESERVE = 'reserve' + UNRESERVE = 'unreserve' + PAUSE = 'pause' + UNPAUSE = 'unpause' + + def self.supported_type?(value) + return false unless value.is_a?(String) + [CREATION, DEPLOY, RESERVE, UNRESERVE, PAUSE, UNPAUSE].include?(value) + end end attr_accessor :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by, :reserved_by, :stack, :run_list @@ -108,6 +114,7 @@ module Devops end def set_last_operation(operation_type) + raise ArgumentError unless OperationType.supported_type?(operation_type) self.last_operation_type = operation_type self.last_operation_at = Time.now end diff --git a/devops-service/db/mongo/models/stack/stack_base.rb b/devops-service/db/mongo/models/stack/stack_base.rb index cf118e5..a6bea8c 100644 --- a/devops-service/db/mongo/models/stack/stack_base.rb +++ b/devops-service/db/mongo/models/stack/stack_base.rb @@ -19,37 +19,37 @@ module Devops run_list: {type: Array, value_type: String, empty: true, nil: true} # details: {type: Hash, nil: true} # Hash type isn't supported yet - set_field_validators :id, [::Validators::FieldValidator::NotNil, + set_field_validators :id, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :provider, [::Validators::FieldValidator::NotNil, + set_field_validators :provider, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :project, [::Validators::FieldValidator::NotNil, + set_field_validators :project, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :deploy_env, [::Validators::FieldValidator::NotNil, + set_field_validators :deploy_env, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :stack_template, [::Validators::FieldValidator::NotNil, + set_field_validators :stack_template, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :name, [::Validators::FieldValidator::Nil, + set_field_validators :name, ::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :owner, [::Validators::FieldValidator::NotNil, + set_field_validators :owner, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :run_list, [::Validators::FieldValidator::NotNil, + set_field_validators :run_list, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::Array, - ::Validators::FieldValidator::RunList] + ::Validators::FieldValidator::RunList def initialize attrs={} # self.provider = self.class.provider diff --git a/devops-service/db/mongo/models/stack_template/stack_template_base.rb b/devops-service/db/mongo/models/stack_template/stack_template_base.rb index 7487724..6e43166 100644 --- a/devops-service/db/mongo/models/stack_template/stack_template_base.rb +++ b/devops-service/db/mongo/models/stack_template/stack_template_base.rb @@ -16,22 +16,22 @@ module Devops template_body: {type: String, empty: false}, owner: {type: String, empty: false} - set_field_validators :id, [::Validators::FieldValidator::NotNil, + set_field_validators :id, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::Name] + ::Validators::FieldValidator::Name - set_field_validators :provider, [::Validators::FieldValidator::NotNil, + set_field_validators :provider, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :template_body, [::Validators::FieldValidator::NotNil, + set_field_validators :template_body, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty - set_field_validators :owner, [::Validators::FieldValidator::NotNil, + set_field_validators :owner, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::NotEmpty set_validators ::Validators::StackTemplate::TemplateContent diff --git a/devops-service/db/mongo/models/user.rb b/devops-service/db/mongo/models/user.rb index c53a8dd..89d21fa 100644 --- a/devops-service/db/mongo/models/user.rb +++ b/devops-service/db/mongo/models/user.rb @@ -15,6 +15,8 @@ module Devops KNOWN_ENTITIES = %w(flavor group image project server key user filter network provider script templates stack_template stack) + attr_accessor :id, :password, :privileges, :email + set_field_validators :id, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::Name diff --git a/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb b/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb index 139397a..5b622b9 100644 --- a/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb +++ b/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb @@ -25,12 +25,6 @@ RSpec.describe Devops::Model::DeployEnvEc2, type: :model do end end - describe '#to_hash' do - it 'includes vpc_id' do - expect(env.to_hash).to include('vpc_id') - end - end - describe '.create' do it 'returns instance of DeployEnvEc2' do expect(described_class.create({})).to be_an_instance_of(described_class) From c7307a339fb96760aa8fdadc0b89832dba6d8f73 Mon Sep 17 00:00:00 2001 From: amartynov Date: Mon, 23 Nov 2015 14:55:53 +0300 Subject: [PATCH 17/18] #879: tags methods --- .../lib/executors/server_executor.rb | 12 ++++- devops-service/providers/ec2.rb | 47 +++++++------------ 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/devops-service/lib/executors/server_executor.rb b/devops-service/lib/executors/server_executor.rb index 1d57df3..ade9585 100644 --- a/devops-service/lib/executors/server_executor.rb +++ b/devops-service/lib/executors/server_executor.rb @@ -74,8 +74,18 @@ module Devops @server end + def create_server_object options + Devops::Model::Server.new({ + "project" => @project.id, + "deploy_env" => @deploy_env.identifier, + "created_by" => options["created_by"], + "provider" => @deploy_env.provider, + "provider_account" => @deploy_env.provider_account + }) + end + def create_server options - @server = Devops::Model::Server.new({"project" => @project.id, "deploy_env" => @deploy_env.identifier, "created_by" => options["created_by"], "provider" => @deploy_env.provider, "provider_account" => @deploy_env.provider_account}) + @server = create_server_object(options) @server.set_last_operation(Devops::Model::Server::OperationType::CREATION) provider = @deploy_env.provider_instance mongo = ::Devops::Db.connector diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 52644f8..6a90bcc 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -152,7 +152,8 @@ module Provider end s.public_ip = details["ipAddress"] s.private_ip = details["privateIpAddress"] - tags = set_server_tags(s) + tags = server_tags(s) + compute.create_tags(s.id, tags) out << "\nServer tags: #{tags.inspect}\n" out << "\nDone\n\n" out << s.info @@ -160,16 +161,14 @@ module Provider true end - def set_server_tags s - tags = { - "Name" => s.chef_node_name, - "cid:project" => s.project, - "cid:deployEnv" => s.deploy_env, - "cid:user" => s.created_by, - "cid:remoteUser" => s.remote_user + def server_tags server + { + "Name" => server.chef_node_name, + "cid:project" => server.project, + "cid:deployEnv" => server.deploy_env, + "cid:user" => server.created_by, + "cid:remoteUser" => server.remote_user } - compute.create_tags(s.id, tags) - tags end def delete_server s @@ -231,18 +230,11 @@ module Provider 'TemplateBody' => stack.template_body, 'Parameters' => stack.parameters || {}, 'Capabilities' => ['CAPABILITY_IAM'], - 'Tags' => { - "StackName" => stack.name, - "StackTemplate" => stack.stack_template, - "cid:project" => stack.project, - "cid:deployEnv" => stack.deploy_env, - "cid:user" => stack.owner - } + 'Tags' => stack_tags(stack) } ) stack.id = response.body['StackId'] out << "Stack id: #{stack.id}\n" - #set_stack_tags(stack, out) out.flush rescue Excon::Errors::Conflict => e raise ProviderErrors::NameConflict @@ -260,20 +252,13 @@ module Provider end end - def set_stack_tags stack, out="" - tags = { -# "cid:remoteUser" => s.remote_user + def stack_tags stack + { + "StackTemplate" => stack.stack_template, + "cid:project" => stack.project, + "cid:deployEnv" => stack.deploy_env, + "cid:user" => stack.owner } - #ids = stack_resources(stack).map {|resource| resource['PhysicalResourceId']} - #ids << stack.id - #compute.create_tags(ids, tags) - stack_resources(stack).each do |resource| - begin - compute.create_tags(resource['PhysicalResourceId'], tags) - rescue Fog::Compute::AWS::Error => e - out << "Error: " + e.message - end - end end def validate_stack_template template From a2d9e57d09200a861b874bf4ef91089e22c0326a Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Mon, 23 Nov 2015 17:21:43 +0300 Subject: [PATCH 18/18] #648: store last server operation as subhash --- devops-service/app/api2/handlers/deploy.rb | 2 +- devops-service/app/api2/handlers/project.rb | 2 +- devops-service/app/api2/handlers/server.rb | 14 ++++++------- devops-service/app/api2/handlers/stack.rb | 2 +- devops-service/db/mongo/models/server.rb | 21 ++++++++++--------- .../lib/executors/server_executor.rb | 15 +++++++------ devops-service/spec/factories/server.rb | 3 +-- devops-service/spec/models/server_spec.rb | 18 ++++++++-------- devops-service/workers/bootstrap_worker.rb | 2 +- devops-service/workers/deploy_worker.rb | 2 +- 10 files changed, 42 insertions(+), 39 deletions(-) diff --git a/devops-service/app/api2/handlers/deploy.rb b/devops-service/app/api2/handlers/deploy.rb index acaf5b1..b0e3045 100644 --- a/devops-service/app/api2/handlers/deploy.rb +++ b/devops-service/app/api2/handlers/deploy.rb @@ -85,7 +85,7 @@ module Devops begin deploy_info = create_deploy_info(s, project, body["build_number"]) deploy_info["run_list"] = run_list if run_list - res = Devops::Executor::ServerExecutor.new(s, out).deploy_server_with_tags(tags, deploy_info) + res = Devops::Executor::ServerExecutor.new(s, out, current_user: owner).deploy_server_with_tags(tags, deploy_info) status.push(res) rescue DeployInfoError => e msg = "Can not get deploy info: " + e.message diff --git a/devops-service/app/api2/handlers/project.rb b/devops-service/app/api2/handlers/project.rb index c771eae..3e36bb9 100644 --- a/devops-service/app/api2/handlers/project.rb +++ b/devops-service/app/api2/handlers/project.rb @@ -225,7 +225,7 @@ module Devops # мы не можем указать один build_number для всех окружений, поэтому nil deploy_info_buf[s.deploy_env] = project.deploy_info(deploy_env_model, nil) end - status.push(Devops::Executor::ServerExecutor.new(s, out).deploy_server(deploy_info)) + status.push(Devops::Executor::ServerExecutor.new(s, out, current_user: parser.current_user).deploy_server(deploy_info)) end status end diff --git a/devops-service/app/api2/handlers/server.rb b/devops-service/app/api2/handlers/server.rb index 448ec1e..6b35d72 100644 --- a/devops-service/app/api2/handlers/server.rb +++ b/devops-service/app/api2/handlers/server.rb @@ -120,7 +120,7 @@ module Devops provider = s.provider_instance r = provider.pause_server s if r.nil? - set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::PAUSE) + set_last_operation_and_save(s, Devops::Model::Server::OperationType::PAUSE) "Server with instance ID '#{s.id}' and node name '#{node_name}' is paused" else @@ -135,7 +135,7 @@ module Devops provider = s.provider_instance r = provider.unpause_server s if r.nil? - set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::UNPAUSE) + set_last_operation_and_save(s, Devops::Model::Server::OperationType::UNPAUSE) "Server with instance ID '#{s.id}' and node name '#{node_name}' is unpaused" else @@ -149,7 +149,7 @@ module Devops Devops::Db.connector.check_project_auth s.project, s.deploy_env, user raise ConflictException.new("Server '#{node_name}' already reserved") unless s.reserved_by.nil? s.reserved_by = user - set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::RESERVE) + set_last_operation_and_save(s, Devops::Model::Server::OperationType::RESERVE) end def unreserve_server node_name @@ -157,7 +157,7 @@ module Devops Devops::Db.connector.check_project_auth s.project, s.deploy_env, parser.current_user raise ConflictException.new("Server '#{node_name}' is not reserved") if s.reserved_by.nil? s.reserved_by = nil - set_last_operation_type_and_save(s, Devops::Model::Server::OperationType::UNRESERVE) + set_last_operation_and_save(s, Devops::Model::Server::OperationType::UNRESERVE) end # TODO: check bootstrap template name @@ -169,7 +169,7 @@ module Devops DevopsLogger.logger.debug "Bootstrap certificate path: #{cert.path}" #bootstrap s, out, cert.path, logger options[:cert_path] = cert.path - executor = Devops::Executor::ServerExecutor.new(s, out) + executor = Devops::Executor::ServerExecutor.new(s, out, current_user: parser.current_user) r = executor.two_phase_bootstrap(options) str = nil r = if check_server(s) @@ -322,8 +322,8 @@ module Devops end end - def set_last_operation_type_and_save(server, operation_type) - server.set_last_operation(operation_type) + def set_last_operation_and_save(server, operation_type) + server.set_last_operation(operation_type, parser.current_user) Devops::Db.connector.server_update(server) end diff --git a/devops-service/app/api2/handlers/stack.rb b/devops-service/app/api2/handlers/stack.rb index c6b122d..6f5a910 100644 --- a/devops-service/app/api2/handlers/stack.rb +++ b/devops-service/app/api2/handlers/stack.rb @@ -155,7 +155,7 @@ module Devops deploy_info = project.deploy_info(stack.deploy_env, nil) servers.each do |s| begin - res = Devops::Executor::ServerExecutor.new(s, out).deploy_server_with_tags(tags, deploy_info) + res = Devops::Executor::ServerExecutor.new(s, out, current_user: owner).deploy_server_with_tags(tags, deploy_info) status.push(res) rescue DeployInfoError => e msg = "Can not get deploy info: " + e.message diff --git a/devops-service/db/mongo/models/server.rb b/devops-service/db/mongo/models/server.rb index aae8ecf..19078ca 100644 --- a/devops-service/db/mongo/models/server.rb +++ b/devops-service/db/mongo/models/server.rb @@ -25,7 +25,7 @@ module Devops end attr_accessor :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by, :reserved_by, :stack, :run_list - attr_accessor :key, :last_operation_at, :last_operation_type + attr_accessor :key, :last_operation types :id => {:type => String, :empty => false}, :provider => {:type => String, :empty => false}, @@ -45,13 +45,13 @@ module Devops def self.fields ["chef_node_name", "project", "deploy_env", "provider", "remote_user", "private_ip", "public_ip", "created_at", "created_by", "key", "reserved_by", "run_list", "stack", - "last_operation_type", "last_operation_at"] + "last_operation"] end def initialize s={} self.set_provider(s) self.chef_node_name = s["chef_node_name"] - self.id = s["_id"] + self.id = s["_id"] || s['id'] self.remote_user = s["remote_user"] self.project = s["project"] self.deploy_env = s["deploy_env"] @@ -63,8 +63,7 @@ module Devops self.reserved_by = s["reserved_by"] self.stack = s["stack"] self.run_list = s["run_list"] || [] - self.last_operation_at = s["last_operation_at"] - self.last_operation_type = s["last_operation_type"] + self.last_operation = s["last_operation"] self end @@ -89,8 +88,7 @@ module Devops "reserved_by" => self.reserved_by, "stack" => stack, "run_list" => self.run_list, - "last_operation_at" => self.last_operation_at, - "last_operation_type" => self.last_operation_type + "last_operation" => self.last_operation }.merge(provider_hash).delete_if { |k,v| v.nil? } end @@ -113,10 +111,13 @@ module Devops self.provider == Provider::Static::PROVIDER end - def set_last_operation(operation_type) + def set_last_operation(operation_type, user) raise ArgumentError unless OperationType.supported_type?(operation_type) - self.last_operation_type = operation_type - self.last_operation_at = Time.now + self.last_operation = { + 'type' => operation_type, + 'date' => Time.now, + 'user' => user + } end end diff --git a/devops-service/lib/executors/server_executor.rb b/devops-service/lib/executors/server_executor.rb index cb983d1..6382005 100644 --- a/devops-service/lib/executors/server_executor.rb +++ b/devops-service/lib/executors/server_executor.rb @@ -35,7 +35,7 @@ module Devops before_deploy :create_run_list - def initialize server, out + def initialize server, out, options={} if server @project = Devops::Db.connector.project(server.project) @deploy_env = @project.deploy_env(server.deploy_env) @@ -44,6 +44,7 @@ module Devops @server = server @out = out @out.class.send(:define_method, :flush) { } unless @out.respond_to?(:flush) + @current_user = options[:current_user] end def self.result_code(symbolic_code) @@ -76,7 +77,7 @@ module Devops def create_server options @server = Devops::Model::Server.new({"project" => @project.id, "deploy_env" => @deploy_env.identifier, "created_by" => options["created_by"], "provider" => @deploy_env.provider, "provider_account" => @deploy_env.provider_account}) - @server.set_last_operation(Devops::Model::Server::OperationType::CREATION) + @server.set_last_operation(Devops::Model::Server::OperationType::CREATION, options["created_by"]) provider = @deploy_env.provider_instance mongo = ::Devops::Db.connector begin @@ -389,17 +390,19 @@ module Devops 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 + + if lline && lline[r] @out << "\nAfter deploy hooks...\n" res = self.run_hook(:after_deploy, @out, deploy_info) @out << "Done\n" - @server.set_last_operation(Devops::Model::Server::OperationType::DEPLOY) + @server.set_last_operation(Devops::Model::Server::OperationType::DEPLOY, @current_user) Devops::Db.connector.server_update(@server) 0 + else + @out << "An error occured during knife command executing" + 1 end end diff --git a/devops-service/spec/factories/server.rb b/devops-service/spec/factories/server.rb index 2878ec8..2af6793 100644 --- a/devops-service/spec/factories/server.rb +++ b/devops-service/spec/factories/server.rb @@ -13,7 +13,6 @@ FactoryGirl.define do chef_node_name 'chef_node_name' reserved_by 'root' run_list [] - last_operation_type 'creation' - last_operation_at Time.now + last_operation({'type' => 'creation', 'date' => Time.now, 'user' => 'root' }) end end \ No newline at end of file diff --git a/devops-service/spec/models/server_spec.rb b/devops-service/spec/models/server_spec.rb index b018979..f970df8 100644 --- a/devops-service/spec/models/server_spec.rb +++ b/devops-service/spec/models/server_spec.rb @@ -40,7 +40,7 @@ RSpec.describe Devops::Model::Server, type: :model do expect(described_class.fields).to match_array(%w( chef_node_name project deploy_env provider remote_user private_ip public_ip created_at created_by key reserved_by - run_list stack last_operation_type last_operation_at + run_list stack last_operation )) end end @@ -69,26 +69,26 @@ RSpec.describe Devops::Model::Server, type: :model do end describe '#set_last_operation' do - it 'sets #last_operation_at to Time.now' do - prev_operation_time = server.last_operation_at - server.set_last_operation('deploy') - expect(server.last_operation_at).to be > prev_operation_time + it 'sets #last_operation.date to Time.now' do + prev_operation_time = server.last_operation['date'] + server.set_last_operation('deploy', 'root') + expect(server.last_operation['date']).to be > prev_operation_time end it 'updates last_operation_type' do - server.set_last_operation('deploy') - expect(server.last_operation_type).to eq 'deploy' + server.set_last_operation('deploy', 'root') + expect(server.last_operation['type']).to eq 'deploy' end it 'it raises error if given type is not supported' do expect { - server.set_last_operation(:wrong) + server.set_last_operation(:wrong, 'root') }.to raise_error ArgumentError end it 'expects string as an argument' do expect { - server.set_last_operation(:deploy) + server.set_last_operation(:deploy, 'root') }.to raise_error ArgumentError end end diff --git a/devops-service/workers/bootstrap_worker.rb b/devops-service/workers/bootstrap_worker.rb index a261900..001f0d2 100644 --- a/devops-service/workers/bootstrap_worker.rb +++ b/devops-service/workers/bootstrap_worker.rb @@ -21,7 +21,7 @@ class BootstrapWorker < Worker bootstrap_template: bootstrap_template } =end - executor = Devops::Executor::ServerExecutor.new(server, out) + executor = Devops::Executor::ServerExecutor.new(server, out, current_user: owner) executor.report = report status = executor.two_phase_bootstrap(options) mongo.set_report_server_data(jid, server.chef_node_name, server.public_ip || server.private_ip) diff --git a/devops-service/workers/deploy_worker.rb b/devops-service/workers/deploy_worker.rb index 9916ee2..b6e48d0 100644 --- a/devops-service/workers/deploy_worker.rb +++ b/devops-service/workers/deploy_worker.rb @@ -16,7 +16,7 @@ class DeployWorker < Worker server = Devops::Model::Server.new(server_attrs) report = save_report(file, owner, server) - executor = Devops::Executor::ServerExecutor.new(server, out) + executor = Devops::Executor::ServerExecutor.new(server, out, current_user: owner) executor.report = report executor.deploy_server_with_tags(tags, deploy_info) end