Merge branch 'specs' into async_servers_deleting
This commit is contained in:
commit
f97f2fe751
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@ devops-service/tests/features/support/config.yml
|
||||
.devops_files/
|
||||
devops-service/plugins
|
||||
devops-service/spec/examples.txt
|
||||
devops-service/coverage
|
||||
devops-service/tmp
|
||||
@ -36,4 +36,6 @@ end
|
||||
group :devepoment do
|
||||
gem 'byebug'
|
||||
gem 'guard-rspec', require: false
|
||||
gem 'simplecov', require: false
|
||||
gem 'simplecov-rcov', require: false
|
||||
end
|
||||
|
||||
@ -60,6 +60,7 @@ GEM
|
||||
gherkin (~> 2.12.0)
|
||||
daemons (1.2.3)
|
||||
diff-lcs (1.2.5)
|
||||
docile (1.1.5)
|
||||
em-websocket (0.3.8)
|
||||
addressable (>= 2.1.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
@ -291,6 +292,13 @@ GEM
|
||||
json
|
||||
redis (>= 3.0.6)
|
||||
redis-namespace (>= 1.3.1)
|
||||
simplecov (0.11.1)
|
||||
docile (~> 1.1.0)
|
||||
json (~> 1.8)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.0)
|
||||
simplecov-rcov (0.2.3)
|
||||
simplecov (>= 0.4.1)
|
||||
sinatra (1.4.5)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.4)
|
||||
@ -352,9 +360,14 @@ DEPENDENCIES
|
||||
rspec (~> 3.3)
|
||||
rspec_junit_formatter
|
||||
sidekiq (= 3.2.6)
|
||||
simplecov
|
||||
simplecov-rcov
|
||||
sinatra (= 1.4.5)
|
||||
sinatra-contrib
|
||||
sinatra-websocket
|
||||
test-unit
|
||||
thin (~> 1.5.1)
|
||||
wisper
|
||||
|
||||
BUNDLED WITH
|
||||
1.11.2
|
||||
|
||||
@ -42,4 +42,5 @@ guard :rspec, cmd: "rspec" do
|
||||
|
||||
# Devops files
|
||||
watch(%r{db/.+\.rb}) { rspec.spec_dir }
|
||||
watch(%r{lib/executors/.+\.rb}) { "#{rspec.spec_dir}/executors" }
|
||||
end
|
||||
|
||||
@ -27,14 +27,14 @@ module Devops
|
||||
def add_account provider
|
||||
account = ::Provider::ProviderFactory.get_accounts_factory(provider).create_account(parser.account)
|
||||
account.validate_fields!
|
||||
Devops::Db.connector.provider_accounts_insert(account)
|
||||
Devops::Db.connector.provider_account_insert(account)
|
||||
::Provider::ProviderFactory.add_account(provider, account)
|
||||
account.to_hash
|
||||
end
|
||||
|
||||
def delete_account name, provider
|
||||
account = Devops::Db.connector.provider_account(provider, name)
|
||||
Devops::Db.connector.provider_accounts_delete(name)
|
||||
Devops::Db.connector.provider_account_delete(name)
|
||||
::Provider::ProviderFactory.delete_account(provider, account)
|
||||
account.to_hash
|
||||
end
|
||||
|
||||
@ -38,6 +38,13 @@ class KnifeCommands
|
||||
knife("tag delete #{name} #{tagsStr}")
|
||||
end
|
||||
|
||||
# extracted from server_executor.rb
|
||||
def swap_tags(name, from, to)
|
||||
tags_delete(name, from)
|
||||
result = tags_create(name, to)
|
||||
!!result[1]
|
||||
end
|
||||
|
||||
def create_role role_name, project, env
|
||||
file = "/tmp/new_role.json"
|
||||
File.open(file, "w") do |f|
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
require "wisper"
|
||||
require "lib/hash_ext"
|
||||
require "lib/nil_class_ext"
|
||||
require "lib/string_ext"
|
||||
require "lib/core_ext/hash"
|
||||
require "lib/core_ext/nil_class"
|
||||
require "lib/core_ext/string"
|
||||
|
||||
require_relative "devops-loader"
|
||||
require_relative "devops-application"
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
require 'lib/string_helper'
|
||||
|
||||
module Connectors
|
||||
module Helpers
|
||||
module DeleteCommand
|
||||
@ -9,7 +7,7 @@ module Connectors
|
||||
# We need this alias to forward methods from MongoConnector to resources connectors.
|
||||
|
||||
def self.included(base)
|
||||
resource_name = StringHelper.underscore_class(base)
|
||||
resource_name = base.to_s.underscore_class
|
||||
method_name = "#{resource_name}_delete".to_sym
|
||||
alias_method method_name, :delete
|
||||
end
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
require 'lib/string_helper'
|
||||
|
||||
module Connectors
|
||||
module Helpers
|
||||
module InsertCommand
|
||||
@ -9,7 +7,7 @@ module Connectors
|
||||
# We need this alias to forward methods from MongoConnector to resources connectors.
|
||||
|
||||
def self.included(base)
|
||||
resource_name = StringHelper.underscore_class(base)
|
||||
resource_name = base.to_s.underscore_class
|
||||
method_name = "#{resource_name}_insert".to_sym
|
||||
alias_method method_name, :insert
|
||||
end
|
||||
@ -24,7 +22,7 @@ module Connectors
|
||||
rescue Mongo::OperationFailure => e
|
||||
# exception's message doesn't always start from error code
|
||||
if e.message =~ /11000/
|
||||
resource_name = StringHelper.underscore_class(record.class)
|
||||
resource_name = record.class.to_s.underscore_class
|
||||
raise InvalidRecord.new("Duplicate key error: #{resource_name} with id '#{record.id}'")
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
require 'lib/string_helper'
|
||||
|
||||
module Connectors
|
||||
module Helpers
|
||||
module ListCommand
|
||||
@ -9,9 +7,7 @@ module Connectors
|
||||
# We need this alias to forward methods from MongoConnector to resources connectors.
|
||||
|
||||
def self.included(base)
|
||||
resource_name = StringHelper.underscore_class(base).to_sym
|
||||
method_name = StringHelper.pluralize(resource_name)
|
||||
alias_method method_name, :list
|
||||
alias_method base.to_s.underscore_class.pluralize, :list
|
||||
end
|
||||
|
||||
# query options is needed, for example, for fields limiting
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
require 'lib/string_helper'
|
||||
|
||||
module Connectors
|
||||
module Helpers
|
||||
module ShowCommand
|
||||
@ -9,7 +7,7 @@ module Connectors
|
||||
# We need this alias to forward methods from MongoConnector to resources connectors.
|
||||
|
||||
def self.included(base)
|
||||
method_name = StringHelper.underscore_class(base).to_sym
|
||||
method_name = base.to_s.underscore_class.to_sym
|
||||
alias_method method_name, :show
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
require 'lib/string_helper'
|
||||
|
||||
module Connectors
|
||||
module Helpers
|
||||
module UpdateCommand
|
||||
@ -8,8 +6,7 @@ module Connectors
|
||||
# We need second method name to forward methods from MongoConnector to resources connectors.
|
||||
|
||||
def self.included(base)
|
||||
resource_name = StringHelper.underscore_class(base)
|
||||
method_name = "#{resource_name}_update".to_sym
|
||||
method_name = "#{base.to_s.underscore_class}_update".to_sym
|
||||
alias_method method_name, :update
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
module Connectors
|
||||
class ProviderAccounts < Base
|
||||
class ProviderAccount < Base
|
||||
|
||||
include Helpers::InsertCommand,
|
||||
Helpers::DeleteCommand
|
||||
@ -16,7 +16,7 @@ module Connectors
|
||||
def provider_account provider, account
|
||||
c = Provider::ProviderFactory.get_account_class(provider)
|
||||
bson = collection.find({provider: provider, _id: account}).to_a.first
|
||||
raise RecordNotFound.new("'Account #{account}' for provider '#{provider}' not found") unless bson
|
||||
raise RecordNotFound.new("Account '#{account}' for provider '#{provider}' not found") unless bson
|
||||
c.build_from_bson(bson)
|
||||
end
|
||||
|
||||
@ -53,7 +53,7 @@ module Devops
|
||||
def subnets_filter
|
||||
networks = provider_instance.networks
|
||||
|
||||
unless self.subnets.empty?
|
||||
if subnets && !subnets.empty?
|
||||
network = networks.detect {|n| n["name"] == self.subnets[0]}
|
||||
if network
|
||||
{"vpc-id" => network["vpcId"] }
|
||||
|
||||
@ -73,10 +73,6 @@ module Devops
|
||||
}
|
||||
end
|
||||
|
||||
def self.create_from_json! json
|
||||
Image.new( JSON.parse(json) )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -30,10 +30,6 @@ module Devops
|
||||
key
|
||||
end
|
||||
|
||||
def self.create_from_json json
|
||||
Key.new( JSON.parse(json) )
|
||||
end
|
||||
|
||||
def filename
|
||||
File.basename(self.path)
|
||||
end
|
||||
|
||||
@ -22,6 +22,7 @@ module Devops
|
||||
#define_hook :after_add_deploy_env
|
||||
|
||||
attr_accessor :id, :deploy_envs, :type, :archived, :description, :run_list
|
||||
attr_accessor :components
|
||||
|
||||
MULTI_TYPE = "multi"
|
||||
|
||||
|
||||
@ -61,6 +61,12 @@ module Devops
|
||||
}
|
||||
end
|
||||
|
||||
# absent of "id" attribute can cause some inconviniences.
|
||||
# for example, we have "record.id" call in InsertCommand
|
||||
def id
|
||||
account_name
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -16,10 +16,6 @@ module Devops
|
||||
super.merge(template_url: template_url)
|
||||
end
|
||||
|
||||
def delete_template_file_from_storage
|
||||
raise 'Implement me'
|
||||
end
|
||||
|
||||
def update_template_url
|
||||
self.template_url = generate_template_file_and_upload_to_storage(id, template_body)
|
||||
end
|
||||
|
||||
@ -75,17 +75,12 @@ module Devops
|
||||
user
|
||||
end
|
||||
|
||||
def self.create_from_json json
|
||||
User.new( JSON.parse(json) )
|
||||
end
|
||||
|
||||
def to_hash_without_id
|
||||
o = {
|
||||
{
|
||||
"email" => self.email,
|
||||
"password" => self.password,
|
||||
"privileges" => self.privileges
|
||||
}
|
||||
o
|
||||
end
|
||||
|
||||
def check_privileges cmd, required_privelege
|
||||
|
||||
@ -33,7 +33,7 @@ class MongoConnector
|
||||
[:keys, :key, :key_insert, :key_delete] => :keys_connector,
|
||||
[:save_report, :report, :reports, :set_report_status, :set_report_server_data, :add_report_subreports] => :reports_connector,
|
||||
[:insert_statistic, :search_statistic] => :statistics_connector,
|
||||
[:provider_accounts, :provider_accounts_insert, :provider_accounts_delete, :provider_account] => :provider_accounts_connector
|
||||
[:provider_accounts, :provider_account_insert, :provider_account_delete, :provider_account] => :provider_accounts_connector
|
||||
)
|
||||
|
||||
def initialize(db, host, port=27017, user=nil, password=nil)
|
||||
@ -48,7 +48,7 @@ class MongoConnector
|
||||
private
|
||||
|
||||
def provider_accounts_connector
|
||||
@provider_accounts_connector ||= Connectors::ProviderAccounts.new(@db)
|
||||
@provider_accounts_connector ||= Connectors::ProviderAccount.new(@db)
|
||||
end
|
||||
|
||||
def images_connector
|
||||
|
||||
@ -13,6 +13,7 @@ module Validators
|
||||
raise InvalidRecord.new("An error raised during validation with #{self.class}: #{e.class}: #{e.message}")
|
||||
end
|
||||
|
||||
# :nocov:
|
||||
def valid?
|
||||
raise 'override me'
|
||||
end
|
||||
@ -20,6 +21,7 @@ module Validators
|
||||
def message
|
||||
raise 'override me'
|
||||
end
|
||||
# :nocov:
|
||||
|
||||
class << self
|
||||
private
|
||||
|
||||
@ -3,7 +3,7 @@ module Validators
|
||||
|
||||
def valid?
|
||||
return true unless @model.flavor
|
||||
available_flavors.detect do |flavor|
|
||||
@model.provider_instance.flavors.detect do |flavor|
|
||||
flavor['id'] == @model.flavor
|
||||
end
|
||||
end
|
||||
@ -11,11 +11,5 @@ module Validators
|
||||
def message
|
||||
"Invalid flavor '#{@model.flavor}'."
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def available_flavors
|
||||
@model.provider_instance.flavors
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -3,6 +3,9 @@ module Validators
|
||||
|
||||
def valid?
|
||||
return true if @model.groups.nil?
|
||||
subnets_filter = @model.subnets_filter
|
||||
available_groups = @model.provider_instance.groups(subnets_filter).keys
|
||||
|
||||
@invalid_groups = @model.groups - available_groups
|
||||
@invalid_groups.empty?
|
||||
end
|
||||
@ -10,12 +13,5 @@ module Validators
|
||||
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
|
||||
@ -6,7 +6,7 @@ module Validators
|
||||
|
||||
def valid?
|
||||
return true unless @model.image
|
||||
available_images.detect do |image|
|
||||
get_available_provider_images(::Devops::Db.connector, @model.provider).detect do |image|
|
||||
image["id"] == @model.image
|
||||
end
|
||||
end
|
||||
@ -14,11 +14,5 @@ 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
|
||||
|
||||
@ -4,21 +4,13 @@ module Validators
|
||||
def valid?
|
||||
return true unless @model.stack_template
|
||||
|
||||
available_stack_templates.detect do |template|
|
||||
template['id'] == @model.stack_template
|
||||
Devops::Db.connector.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
|
||||
|
||||
@ -4,7 +4,7 @@ module Validators
|
||||
class Flavor < Base
|
||||
|
||||
def valid?
|
||||
available_flavors.detect do |flavor|
|
||||
@model.provider_instance.flavors.detect do |flavor|
|
||||
flavor['id'] == @value
|
||||
end
|
||||
end
|
||||
@ -12,12 +12,6 @@ module Validators
|
||||
def message
|
||||
"Invalid flavor '#{@value}'."
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def available_flavors
|
||||
@model.provider_instance.flavors
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,7 +7,7 @@ module Validators
|
||||
include ::ImageCommands
|
||||
|
||||
def valid?
|
||||
available_images.detect do |image|
|
||||
get_available_provider_images(::Devops::Db.connector, @model.provider).detect do |image|
|
||||
image["id"] == @value
|
||||
end
|
||||
end
|
||||
@ -15,12 +15,6 @@ 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
|
||||
|
||||
@ -2,6 +2,7 @@ module Validators
|
||||
class Helpers::Users < Base
|
||||
|
||||
def valid?
|
||||
available_users = ::Devops::Db.connector.users_names(@model)
|
||||
@nonexistent_users = (@model || []) - available_users
|
||||
@nonexistent_users.empty?
|
||||
end
|
||||
@ -9,11 +10,5 @@ module Validators
|
||||
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
|
||||
|
||||
@ -8,22 +8,12 @@ module Validators
|
||||
include BootstrapTemplatesCommands
|
||||
|
||||
def valid?
|
||||
if @model.bootstrap_template
|
||||
available_templates.include?(@model.bootstrap_template)
|
||||
else
|
||||
true
|
||||
end
|
||||
get_templates.include?(@model.bootstrap_template)
|
||||
end
|
||||
|
||||
def message
|
||||
"Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def available_templates
|
||||
get_templates
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
27
devops-service/lib/core_ext/string.rb
Normal file
27
devops-service/lib/core_ext/string.rb
Normal file
@ -0,0 +1,27 @@
|
||||
class String
|
||||
def present?
|
||||
!empty?
|
||||
end
|
||||
|
||||
def blank?
|
||||
empty?
|
||||
end
|
||||
|
||||
# from ActiveSupport
|
||||
def underscore
|
||||
gsub(/::/, '/').
|
||||
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
||||
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
||||
tr("-", "_").
|
||||
downcase
|
||||
end
|
||||
|
||||
def underscore_class
|
||||
split('::').last.underscore
|
||||
end
|
||||
|
||||
# rough simplification
|
||||
def pluralize
|
||||
"#{self}s"
|
||||
end
|
||||
end
|
||||
35
devops-service/lib/executors/expiration_scheduler.rb
Normal file
35
devops-service/lib/executors/expiration_scheduler.rb
Normal file
@ -0,0 +1,35 @@
|
||||
require "workers/delete_server_worker"
|
||||
|
||||
module Devops
|
||||
module Executor
|
||||
class ExpirationScheduler
|
||||
def initialize(expires, server)
|
||||
@expires, @server = expires, server
|
||||
end
|
||||
|
||||
def schedule_expiration!
|
||||
return unless @expires
|
||||
DeleteServerWorker.perform_in(interval_in_seconds, server_chef_node_name: @server.chef_node_name)
|
||||
end
|
||||
|
||||
def interval_in_seconds
|
||||
interval = @expires.to_i
|
||||
measure_unit = @expires.chars.last
|
||||
case measure_unit
|
||||
when 's'
|
||||
interval
|
||||
when 'm'
|
||||
interval * 60
|
||||
when 'h'
|
||||
interval * 60 * 60
|
||||
when 'd'
|
||||
interval * 60 * 60 * 24
|
||||
when 'w'
|
||||
interval * 60 * 60 * 24 * 7
|
||||
else
|
||||
raise 'Wrong interval format'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,6 +1,5 @@
|
||||
require "lib/knife/knife_factory"
|
||||
require "workers/worker"
|
||||
require "workers/delete_server_worker"
|
||||
require "lib/executors/expiration_scheduler"
|
||||
require "hooks"
|
||||
require 'net/ssh'
|
||||
|
||||
@ -9,12 +8,16 @@ module Devops
|
||||
class ServerExecutor
|
||||
include Hooks
|
||||
|
||||
RESULT_CODES = {
|
||||
ERROR_CODES = {
|
||||
server_bootstrap_fail: 2,
|
||||
server_cannot_update_tags: 3,
|
||||
server_bootstrap_private_ip_unset: 4,
|
||||
server_not_in_chef_nodes: 5,
|
||||
server_bootstrap_unknown_error: 7,
|
||||
deploy_unknown_error: 6,
|
||||
deploy_failed: 8
|
||||
deploy_failed: 8,
|
||||
creating_server_unknown_error: 9,
|
||||
creating_server_in_cloud_failed: 10
|
||||
}
|
||||
|
||||
# waiting for 5*60 seconds (5 min)
|
||||
@ -34,46 +37,32 @@ module Devops
|
||||
define_hook :before_bootstrap
|
||||
define_hook :after_bootstrap
|
||||
|
||||
before_deploy :create_run_list
|
||||
before_deploy :add_run_list_to_deploy_info
|
||||
|
||||
|
||||
attr_accessor :server, :deploy_env, :report, :project
|
||||
|
||||
def initialize server, out, options={}
|
||||
if server
|
||||
@project = Devops::Db.connector.project(server.project)
|
||||
@deploy_env = @project.deploy_env(server.deploy_env)
|
||||
end
|
||||
@knife_instance = KnifeFactory.instance
|
||||
@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)
|
||||
RESULT_CODES.fetch(symbolic_code)
|
||||
def self.error_code(symbolic_code)
|
||||
ERROR_CODES.fetch(symbolic_code)
|
||||
end
|
||||
|
||||
def self.symbolic_result_code(integer_code)
|
||||
RESULT_CODES.key(integer_code) || :unknown_error
|
||||
def self.symbolic_error_code(integer_code)
|
||||
ERROR_CODES.key(integer_code) || :unknown_error
|
||||
end
|
||||
|
||||
def result_code(symbolic_code)
|
||||
self.class.result_code(symbolic_code)
|
||||
end
|
||||
|
||||
def report= r
|
||||
@report = r
|
||||
end
|
||||
|
||||
def project= p
|
||||
@project = p
|
||||
end
|
||||
|
||||
def deploy_env= e
|
||||
@deploy_env = e
|
||||
end
|
||||
|
||||
def server
|
||||
@server
|
||||
def error_code(symbolic_code)
|
||||
self.class.error_code(symbolic_code)
|
||||
end
|
||||
|
||||
def create_server_object options
|
||||
@ -109,7 +98,9 @@ module Devops
|
||||
res[:before] = self.run_hook :before_create
|
||||
@out << "Done\n"
|
||||
|
||||
return false unless provider.create_server(@server, @deploy_env.image, @deploy_env.flavor, @deploy_env.subnets, @deploy_env.groups, @out)
|
||||
unless provider.create_server(@server, @deploy_env.image, @deploy_env.flavor, @deploy_env.subnets, @deploy_env.groups, @out)
|
||||
return error_code(:creating_server_in_cloud_failed)
|
||||
end
|
||||
mongo.server_insert @server
|
||||
|
||||
@out << "\nAfter create hooks...\n"
|
||||
@ -118,7 +109,7 @@ module Devops
|
||||
@out.flush
|
||||
DevopsLogger.logger.info "Server with parameters: #{@server.to_hash.inspect} is running"
|
||||
|
||||
schedule_expiration(@server)
|
||||
schedule_expiration()
|
||||
|
||||
unless options["without_bootstrap"]
|
||||
bootstrap_options = {
|
||||
@ -135,11 +126,15 @@ module Devops
|
||||
DevopsLogger.logger.error e.message
|
||||
roll_back
|
||||
mongo.server_delete @server.id
|
||||
# return 5
|
||||
return result_code(:server_not_in_chef_nodes)
|
||||
error_code(:creating_server_unknown_error)
|
||||
end
|
||||
end
|
||||
|
||||
# options:
|
||||
# :run_list (optional)
|
||||
# :bootstrap_template (optional)
|
||||
# :chef_environment (optional)
|
||||
# :config (optional)
|
||||
def bootstrap options
|
||||
@out << "\n\nBootstrap...\n"
|
||||
@out.flush
|
||||
@ -151,7 +146,7 @@ module Devops
|
||||
@out << "Done\n"
|
||||
if @server.private_ip.nil?
|
||||
@out << "Error: Private IP is null"
|
||||
return false
|
||||
return error_code(:server_bootstrap_private_ip_unset)
|
||||
end
|
||||
ja = {
|
||||
:provider => @server.provider,
|
||||
@ -166,13 +161,7 @@ module Devops
|
||||
|
||||
address = "#{@server.remote_user}@#{ip}"
|
||||
|
||||
cmd = 'ssh '
|
||||
cmd << "-i #{cert_path} "
|
||||
cmd << '-q '
|
||||
cmd << '-o StrictHostKeyChecking=no '
|
||||
cmd << '-o ConnectTimeout=2 -o ConnectionAttempts=1 '
|
||||
cmd << "#{address} 'exit'"
|
||||
cmd << " 2>&1"
|
||||
cmd = check_ssh_command(cert_path, address)
|
||||
|
||||
@out << "\nWaiting for SSH..."
|
||||
@out << "\nTest command: '#{cmd}'\n"
|
||||
@ -181,16 +170,16 @@ module Devops
|
||||
retries_amount = 0
|
||||
begin
|
||||
sleep(5)
|
||||
res = `#{cmd}`
|
||||
res = execute_system_command(cmd)
|
||||
retries_amount += 1
|
||||
if retries_amount > MAX_SSH_RETRIES_AMOUNT
|
||||
if retries_amount >= MAX_SSH_RETRIES_AMOUNT
|
||||
@out.puts "Can not connect to #{address}"
|
||||
@out.puts res
|
||||
@out.flush
|
||||
DevopsLogger.logger.error "Can not connect with command '#{cmd}':\n#{res}"
|
||||
return result_code(:server_bootstrap_fail)
|
||||
return error_code(:server_bootstrap_fail)
|
||||
end
|
||||
raise ArgumentError.new("Can not connect with command '#{cmd}' ") unless $?.success?
|
||||
raise ArgumentError.new("Can not connect with command '#{cmd}' ") unless last_command_successful?
|
||||
rescue ArgumentError => e
|
||||
@out.puts "SSH command failed, retry (#{retries_amount}/#{MAX_SSH_RETRIES_AMOUNT})"
|
||||
@out.flush
|
||||
@ -200,7 +189,7 @@ module Devops
|
||||
provider = @server.provider_instance
|
||||
@server.chef_node_name = provider.create_default_chef_node_name(@server) if @server.chef_node_name.nil?
|
||||
|
||||
r = @knife_instance.knife_bootstrap(@out, ip, self.bootstrap_options(ja, options))
|
||||
r = knife_instance.knife_bootstrap(@out, ip, self.bootstrap_options(ja, options))
|
||||
|
||||
if r == 0
|
||||
@out << "Chef node name: #{@server.chef_node_name}\n"
|
||||
@ -217,10 +206,16 @@ module Devops
|
||||
else
|
||||
@out << "Can not bootstrap node '#{@server.id}', error code: #{r}"
|
||||
@out.flush
|
||||
result_code(:server_bootstrap_fail)
|
||||
error_code(:server_bootstrap_fail)
|
||||
end
|
||||
end
|
||||
|
||||
# options:
|
||||
# :cert_path (required)
|
||||
# :run_list (optional)
|
||||
# :bootstrap_template (optional)
|
||||
# :chef_environment (optional)
|
||||
# :config (optional)
|
||||
def bootstrap_options attributes, options
|
||||
bootstrap_options = [
|
||||
"-x #{@server.remote_user}",
|
||||
@ -242,6 +237,7 @@ module Devops
|
||||
@out << "Done\n"
|
||||
end
|
||||
|
||||
# essentially, it just bootstrap and then deploy
|
||||
def two_phase_bootstrap options
|
||||
prepare_two_phase_bootstrap(options)
|
||||
# bootstrap phase
|
||||
@ -252,14 +248,14 @@ module Devops
|
||||
bootstrap_status = bootstrap(options)
|
||||
|
||||
if bootstrap_status == 0
|
||||
if check_server
|
||||
if check_server_on_chef_server
|
||||
@out << "Server #{@server.chef_node_name} is created"
|
||||
else
|
||||
@out.puts "Can not find client or node on chef-server"
|
||||
roll_back
|
||||
@out.flush
|
||||
mongo.server_delete @server.id
|
||||
return result_code(:server_not_in_chef_nodes)
|
||||
return error_code(:server_not_in_chef_nodes)
|
||||
end
|
||||
else
|
||||
# @out << roll_back
|
||||
@ -269,22 +265,20 @@ module Devops
|
||||
DevopsLogger.logger.error msg
|
||||
@out.puts msg
|
||||
@out.flush
|
||||
return result_code(:server_bootstrap_fail)
|
||||
return error_code(:server_bootstrap_fail)
|
||||
end
|
||||
rescue => e
|
||||
@out << "\nError: #{e.message}\n"
|
||||
@out.flush
|
||||
return result_code(:server_bootstrap_unknown_error)
|
||||
return error_code(:server_bootstrap_unknown_error)
|
||||
end
|
||||
|
||||
# deploy phase. Assume that all servers are bootstraped successfully here.
|
||||
begin
|
||||
#raise "hello"
|
||||
@out << "\n"
|
||||
run_list = compute_run_list
|
||||
@out << "\nComputed run list: #{run_list.join(", ")}"
|
||||
@out << "\n\nComputed run list: #{run_list.join(", ")}"
|
||||
@out.flush
|
||||
@knife_instance.set_run_list(@server.chef_node_name, run_list)
|
||||
knife_instance.set_run_list(@server.chef_node_name, run_list)
|
||||
deploy_info = options[:deploy_info] || @project.deploy_info(@deploy_env)
|
||||
deploy_status = deploy_server(deploy_info)
|
||||
if deploy_status == 0
|
||||
@ -294,19 +288,20 @@ module Devops
|
||||
msg << "\nDeploing server operation status was #{deploy_status}"
|
||||
DevopsLogger.logger.error msg
|
||||
@out << "\n" + msg + "\n"
|
||||
result_code(:deploy_failed)
|
||||
error_code(:deploy_failed)
|
||||
end
|
||||
rescue => e
|
||||
@out << "\nError: #{e.message}\n"
|
||||
DevopsLogger.logger.error(e.message + "\n" + e.backtrace.join("\n"))
|
||||
result_code(:deploy_unknown_error)
|
||||
error_code(:deploy_unknown_error)
|
||||
end
|
||||
end
|
||||
|
||||
def check_server
|
||||
@knife_instance.chef_node_list.include?(@server.chef_node_name) and @knife_instance.chef_client_list.include?(@server.chef_node_name)
|
||||
def check_server_on_chef_server
|
||||
knife_instance.chef_node_list.include?(@server.chef_node_name) and knife_instance.chef_client_list.include?(@server.chef_node_name)
|
||||
end
|
||||
|
||||
# returns a hash with :chef_node, :chef_client and :server keys
|
||||
def unbootstrap
|
||||
k = Devops::Db.connector.key(@server.key)
|
||||
cert_path = k.path
|
||||
@ -346,35 +341,28 @@ module Devops
|
||||
end
|
||||
|
||||
def deploy_server_with_tags tags, deploy_info
|
||||
old_tags_str = nil
|
||||
new_tags_str = nil
|
||||
unless tags.empty?
|
||||
old_tags_str = @knife_instance.tags_list(@server.chef_node_name).join(" ")
|
||||
@out << "Server tags: #{old_tags_str}\n"
|
||||
@knife_instance.tags_delete(@server.chef_node_name, old_tags_str)
|
||||
return deploy_server(deploy_info) if tags.empty?
|
||||
|
||||
new_tags_str = tags.join(" ")
|
||||
@out << "Server new tags: #{new_tags_str}\n"
|
||||
cmd = @knife_instance.tags_create(@server.chef_node_name, new_tags_str)
|
||||
unless cmd[1]
|
||||
m = "Error: Cannot add tags '#{new_tags_str}' to server '#{@server.chef_node_name}'"
|
||||
DevopsLogger.logger.error(m)
|
||||
@out << m + "\n"
|
||||
return 3
|
||||
end
|
||||
DevopsLogger.logger.info("Set tags for '#{@server.chef_node_name}': #{new_tags_str}")
|
||||
old_tags_str = knife_instance.tags_list(@server.chef_node_name).join(" ")
|
||||
new_tags_str = tags.join(" ")
|
||||
|
||||
@out.puts "Temporarily changing tags (#{old_tags_str}) to (#{new_tags_str})"
|
||||
unless knife_instance.swap_tags(@server.chef_node_name, old_tags_str, new_tags_str)
|
||||
m = "Error: Cannot add tags '#{new_tags_str}' to server '#{@server.chef_node_name}'"
|
||||
DevopsLogger.logger.error(m)
|
||||
@out.puts m
|
||||
return error_code(:server_cannot_update_tags)
|
||||
end
|
||||
DevopsLogger.logger.info("Set tags for '#{@server.chef_node_name}': #{new_tags_str}")
|
||||
|
||||
r = deploy_server deploy_info
|
||||
|
||||
unless tags.empty?
|
||||
@out << "Restore tags\n"
|
||||
cmd = @knife_instance.tags_delete(@server.chef_node_name, new_tags_str)
|
||||
DevopsLogger.logger.info("Deleted tags for #{@server.chef_node_name}: #{new_tags_str}")
|
||||
cmd = @knife_instance.tags_create(@server.chef_node_name, old_tags_str)
|
||||
DevopsLogger.logger.info("Set tags for #{@server.chef_node_name}: #{old_tags_str}")
|
||||
begin
|
||||
deploy_result = deploy_server deploy_info
|
||||
ensure
|
||||
@out.puts "Restoring tags"
|
||||
knife_instance.swap_tags(@server.chef_node_name, new_tags_str, old_tags_str)
|
||||
DevopsLogger.logger.info("Restoring tags for #{@server.chef_node_name}: from #{new_tags_str} back to (#{old_tags_str})")
|
||||
end
|
||||
return r
|
||||
deploy_result
|
||||
end
|
||||
|
||||
def deploy_server deploy_info
|
||||
@ -397,7 +385,7 @@ module Devops
|
||||
f.write json
|
||||
end
|
||||
end
|
||||
@out << "Deploy Input Parameters:\n"
|
||||
@out.puts "Deploy Input Parameters:"
|
||||
@out.puts json
|
||||
@out.flush
|
||||
cmd << " -j http://#{DevopsConfig.config[:address]}:#{DevopsConfig.config[:port]}/#{DevopsConfig.config[:url_prefix]}/v2.0/deploy/data/#{file}"
|
||||
@ -412,7 +400,7 @@ module Devops
|
||||
end
|
||||
@out.flush
|
||||
k = Devops::Db.connector.key(@server.key)
|
||||
lline = @knife_instance.ssh_stream(@out, cmd, ip, @server.remote_user, k.path)
|
||||
lline = knife_instance.ssh_stream(@out, cmd, ip, @server.remote_user, k.path)
|
||||
r = /Chef\sClient\sfinished/i
|
||||
|
||||
if lline && lline[r]
|
||||
@ -432,21 +420,11 @@ module Devops
|
||||
|
||||
def delete_from_chef_server node_name
|
||||
{
|
||||
:chef_node => @knife_instance.chef_node_delete(node_name),
|
||||
:chef_client => @knife_instance.chef_client_delete(node_name)
|
||||
:chef_node => knife_instance.chef_node_delete(node_name),
|
||||
:chef_client => knife_instance.chef_client_delete(node_name)
|
||||
}
|
||||
end
|
||||
|
||||
=begin
|
||||
def delete_etc_chef s, cert_path
|
||||
cmd = "ssh -i #{cert_path} -t -q #{s.remote_user}@#{s.private_ip}"
|
||||
cmd += " sudo " unless s.remote_user == "root"
|
||||
cmd += "rm -Rf /etc/chef"
|
||||
r = `#{cmd}`
|
||||
raise(r) unless $?.success?
|
||||
end
|
||||
=end
|
||||
|
||||
def delete_server
|
||||
mongo = ::Devops::Db.connector
|
||||
if @server.static?
|
||||
@ -487,7 +465,7 @@ module Devops
|
||||
end
|
||||
end
|
||||
|
||||
def create_run_list out, deploy_info
|
||||
def add_run_list_to_deploy_info out, deploy_info
|
||||
out << "\nGenerate run list hook...\n"
|
||||
if deploy_info["run_list"]
|
||||
out << "Deploy info already contains 'run_list': #{deploy_info["run_list"].join(", ")}\n"
|
||||
@ -496,14 +474,6 @@ module Devops
|
||||
out << "Project run list: #{@project.run_list.join(", ")}\n"
|
||||
out << "Deploy environment run list: #{@deploy_env.run_list.join(", ")}\n"
|
||||
out << "Server run list: #{@server.run_list.join(", ")}\n"
|
||||
=begin
|
||||
rlist = Set.new.merge(@deploy_env.provider_instance.run_list).merge(@project.run_list).merge(@deploy_env.run_list).merge(@server.run_list)
|
||||
if @server.stack
|
||||
stack = Devops::Db.connector.stack(@server.stack)
|
||||
out << "Stack run list: #{stack.run_list.join(", ")}\n"
|
||||
rlist.merge(stack.run_list)
|
||||
end
|
||||
=end
|
||||
deploy_info["run_list"] = compute_run_list
|
||||
out << "New deploy run list: #{deploy_info["run_list"].join(", ")}\nRun list has been generated\n\n"
|
||||
end
|
||||
@ -511,47 +481,51 @@ module Devops
|
||||
def compute_run_list
|
||||
rlist = []
|
||||
[@deploy_env.provider_instance.run_list, @project.run_list, @deploy_env.run_list, @server.run_list].each do |sub_run_list|
|
||||
rlist += sub_run_list if sub_run_list.is_a?(Array)
|
||||
rlist += sub_run_list if sub_run_list
|
||||
end
|
||||
rlist = Set.new(rlist)
|
||||
if @server.stack
|
||||
stack = Devops::Db.connector.stack(@server.stack)
|
||||
# out << "Stack run list: #{stack.run_list.join(", ")}\n"
|
||||
srl = stack.run_list
|
||||
rlist.merge(srl) if srl.is_a?(Array)
|
||||
rlist += srl if srl
|
||||
end
|
||||
rlist.to_a
|
||||
rlist.uniq
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def schedule_expiration(server)
|
||||
expires = @deploy_env.expires
|
||||
return unless expires
|
||||
interval = interval_in_seconds(expires)
|
||||
@out << "Planning expiration in #{expires}"
|
||||
DeleteServerWorker.perform_in(interval, server_chef_node_name: server.chef_node_name)
|
||||
end
|
||||
|
||||
def interval_in_seconds(interval_as_string)
|
||||
interval = interval_as_string.to_i
|
||||
measure_unit = interval_as_string.chars.last
|
||||
case measure_unit
|
||||
when 's'
|
||||
interval
|
||||
when 'm'
|
||||
interval * 60
|
||||
when 'h'
|
||||
interval * 60 * 60
|
||||
when 'd'
|
||||
interval * 60 * 60 * 24
|
||||
when 'w'
|
||||
interval * 60 * 60 * 24 * 7
|
||||
else
|
||||
raise 'Wrong interval format'
|
||||
def schedule_expiration
|
||||
if @deploy_env.expires
|
||||
@out << "Planning expiration in #{@deploy_env.expires}"
|
||||
ExpirationScheduler.new(@deploy_env.expires, @server).schedule_expiration!
|
||||
end
|
||||
end
|
||||
|
||||
def check_ssh_command(cert_path, address)
|
||||
cmd = 'ssh '
|
||||
cmd << "-i #{cert_path} "
|
||||
cmd << '-q '
|
||||
cmd << '-o StrictHostKeyChecking=no '
|
||||
cmd << '-o ConnectTimeout=2 -o ConnectionAttempts=1 '
|
||||
cmd << "#{address} 'exit'"
|
||||
cmd << " 2>&1"
|
||||
cmd
|
||||
end
|
||||
|
||||
# to simplify testing
|
||||
# :nocov:
|
||||
def execute_system_command(cmd)
|
||||
`#{cmd}`
|
||||
end
|
||||
|
||||
def last_command_successful?
|
||||
$?.success?
|
||||
end
|
||||
|
||||
def knife_instance
|
||||
@knife_instance ||= KnifeFactory.instance
|
||||
end
|
||||
# :nocov:
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
class String
|
||||
def present?
|
||||
!empty?
|
||||
end
|
||||
|
||||
def blank?
|
||||
empty?
|
||||
end
|
||||
end
|
||||
@ -1,35 +0,0 @@
|
||||
module StringHelper
|
||||
extend self
|
||||
|
||||
# from Rails' ActiveSupport
|
||||
def underscore(string)
|
||||
string.gsub(/::/, '/').
|
||||
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
||||
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
||||
tr("-", "_").
|
||||
downcase
|
||||
end
|
||||
|
||||
def underscore_class(klass, without_ancestors=true)
|
||||
class_name = if without_ancestors
|
||||
klass.to_s.split('::').last
|
||||
else
|
||||
klass.to_s
|
||||
end
|
||||
StringHelper.underscore(class_name)
|
||||
end
|
||||
|
||||
# from Rails' ActiveSupport
|
||||
def camelize(term)
|
||||
string = term.to_s
|
||||
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
||||
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
||||
string.gsub!(/\//, '::')
|
||||
string
|
||||
end
|
||||
|
||||
# rough simplification
|
||||
def pluralize(string)
|
||||
"#{string}s"
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,58 @@
|
||||
require 'db/mongo/models/provider_accounts/ec2_provider_account'
|
||||
require 'db/mongo/models/provider_accounts/openstack_provider_account'
|
||||
require 'db/mongo/connectors/provider_account'
|
||||
require 'spec/connectors/tester_connector/provider_account'
|
||||
|
||||
RSpec.describe Connectors::ProviderAccount, type: :connector do
|
||||
set_tester_connector TesterConnector::ProviderAccount
|
||||
|
||||
include_examples 'mongo connector', {
|
||||
model_name: :provider_account,
|
||||
factory_name: :ec2_provider_account,
|
||||
only: [:insert, :delete],
|
||||
field_to_update: :description
|
||||
}
|
||||
|
||||
describe '#provider_accounts', cleanup_after: :all do
|
||||
before(:all) do
|
||||
@tester_connector.create(id: 'foo', provider: 'ec2')
|
||||
@tester_connector.create(id: 'bar', provider: 'openstack')
|
||||
end
|
||||
|
||||
it 'returns array of Ec2ProviderAccount if @provider is ec2' do
|
||||
expect(
|
||||
@connector.provider_accounts('ec2')
|
||||
).to be_an_array_of(Devops::Model::Ec2ProviderAccount).and have_size(1)
|
||||
end
|
||||
|
||||
it 'returns array of Ec2ProviderAccount if @provider is openstack' do
|
||||
expect(
|
||||
@connector.provider_accounts('openstack')
|
||||
).to be_an_array_of(Devops::Model::OpenstackProviderAccount).and have_size(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#provider_account', cleanup_after: :all do
|
||||
before(:all) do
|
||||
@tester_connector.create(id: 'foo', provider: 'ec2')
|
||||
@tester_connector.create(id: 'bar', provider: 'openstack')
|
||||
end
|
||||
|
||||
it 'returns ec2 provider account' do
|
||||
acc = @connector.provider_account('ec2', 'foo')
|
||||
expect(acc).to be_a(Devops::Model::Ec2ProviderAccount)
|
||||
expect(acc.account_name).to eq 'foo'
|
||||
end
|
||||
|
||||
it 'returns openstack provider account' do
|
||||
acc = @connector.provider_account('openstack', 'bar')
|
||||
expect(acc).to be_a(Devops::Model::OpenstackProviderAccount)
|
||||
expect(acc.account_name).to eq 'bar'
|
||||
end
|
||||
|
||||
it 'raises error if account is missing' do
|
||||
expect{@connector.provider_account('ec2', 'missing')}.to raise_error(RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@ -0,0 +1,6 @@
|
||||
require_relative 'base'
|
||||
|
||||
module TesterConnector
|
||||
class ProviderAccount < Base
|
||||
end
|
||||
end
|
||||
47
devops-service/spec/executors/expiration_scheduler_spec.rb
Normal file
47
devops-service/spec/executors/expiration_scheduler_spec.rb
Normal file
@ -0,0 +1,47 @@
|
||||
require 'lib/executors/expiration_scheduler'
|
||||
|
||||
RSpec.describe Devops::Executor::ExpirationScheduler do
|
||||
let(:server) { build(:server) }
|
||||
|
||||
describe '#schedule_expiration!' do
|
||||
it 'schedules server deleting at given time' do
|
||||
expect(DeleteServerWorker).to receive(:perform_in).with(120, server_chef_node_name: 'chef_node_name')
|
||||
described_class.new('2m', server).schedule_expiration!
|
||||
end
|
||||
|
||||
it "doesn't schedule job if expires is nil" do
|
||||
expect(DeleteServerWorker).not_to receive(:perform_in)
|
||||
described_class.new(nil, server).schedule_expiration!
|
||||
end
|
||||
end
|
||||
|
||||
describe '#interval_in_seconds' do
|
||||
def interval_in_seconds(expires)
|
||||
described_class.new(expires, server).interval_in_seconds
|
||||
end
|
||||
|
||||
it 'recognizes seconds' do
|
||||
expect(interval_in_seconds('2s')).to eq 2
|
||||
end
|
||||
|
||||
it 'recognizes minutes' do
|
||||
expect(interval_in_seconds('3m')).to eq 180
|
||||
end
|
||||
|
||||
it 'recognizes hours' do
|
||||
expect(interval_in_seconds('1h')).to eq 3600
|
||||
end
|
||||
|
||||
it 'recognizes days' do
|
||||
expect(interval_in_seconds('1d')).to eq 86400
|
||||
end
|
||||
|
||||
it 'recognizes weeks' do
|
||||
expect(interval_in_seconds('1w')).to eq 604800
|
||||
end
|
||||
|
||||
it 'raises on wrong format' do
|
||||
expect { interval_in_seconds('wrong') }.to raise_error(StandardError)
|
||||
end
|
||||
end
|
||||
end
|
||||
792
devops-service/spec/executors/server_executor_spec.rb
Normal file
792
devops-service/spec/executors/server_executor_spec.rb
Normal file
@ -0,0 +1,792 @@
|
||||
require 'lib/executors/server_executor'
|
||||
|
||||
RSpec.describe Devops::Executor::ServerExecutor, type: :executor, stubbed_connector: true, stubbed_logger: true do
|
||||
let(:project) { build(:project) }
|
||||
let(:deploy_env) { project.deploy_env('foo') }
|
||||
let(:server) { build(:server, project: project.id, deploy_env: 'foo') }
|
||||
let(:output) { File.open(File::NULL, "w") }
|
||||
let(:provider) { double('Provider instance') }
|
||||
let(:executor) { described_class.new(server, output) }
|
||||
|
||||
|
||||
before do
|
||||
allow(stubbed_connector).to receive(:project) { project }
|
||||
allow(executor.deploy_env).to receive(:provider_instance) { provider }
|
||||
allow(server).to receive(:provider_instance) { provider }
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'sets server, project, deploy_env, out instance variables' do
|
||||
expect(executor.server).to eq server
|
||||
expect(executor.deploy_env).to eq deploy_env
|
||||
expect(executor).to have_instance_variable_value(:project, project)
|
||||
expect(executor).to have_instance_variable_value(:out, output)
|
||||
end
|
||||
|
||||
it 'defines :flush method on @out if it is absent' do
|
||||
out = Class.new.new
|
||||
expect(out).not_to respond_to(:flush)
|
||||
described_class.new(server, out)
|
||||
expect(out).to respond_to(:flush)
|
||||
end
|
||||
|
||||
it 'sets current_user from options' do
|
||||
user = double
|
||||
executor = described_class.new(server, '', {current_user: user})
|
||||
expect(executor).to have_instance_variable_value(:current_user, user)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#report=' do
|
||||
it 'sets report instance variable' do
|
||||
executor.report= 'foo'
|
||||
expect(executor).to have_instance_variable_value(:report, 'foo')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#project=' do
|
||||
it 'sets project instance variable' do
|
||||
executor.project= 'foo'
|
||||
expect(executor).to have_instance_variable_value(:project, 'foo')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '.symbolic_error_code' do
|
||||
it 'returns symbol given an integer' do
|
||||
expect(described_class.symbolic_error_code(2)).to eq :server_bootstrap_fail
|
||||
end
|
||||
|
||||
it "returns :unknown_error if can't recognize error code" do
|
||||
expect(described_class.symbolic_error_code(123)).to eq :unknown_error
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
describe '#create_server_object' do
|
||||
it 'builds Server object' do
|
||||
server = executor.create_server_object('created_by' => 'me')
|
||||
expect(server).to be_a(Devops::Model::Server)
|
||||
expect(server.project).to eq 'my_project'
|
||||
expect(server.deploy_env).to eq 'foo'
|
||||
expect(server.created_by).to eq 'me'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
describe '#create_server' do
|
||||
let(:image) { double('Image instance', remote_user: 'remote_user') }
|
||||
let(:create_server) {
|
||||
executor.create_server(
|
||||
'created_by' => 'user',
|
||||
'run_list' => @run_list,
|
||||
'name' => 'node_name',
|
||||
'key' => @key,
|
||||
'without_bootstrap' => @without_bootstrap
|
||||
)
|
||||
}
|
||||
|
||||
before do
|
||||
allow(provider).to receive(:create_server) { true }
|
||||
allow(stubbed_connector).to receive(:image) { image }
|
||||
allow(stubbed_connector).to receive(:server_insert)
|
||||
@without_bootstrap = true
|
||||
@run_list = %w(role[asd])
|
||||
@key = 'key'
|
||||
end
|
||||
|
||||
it 'builds server model from given options' do
|
||||
create_server
|
||||
expect(executor.server.created_by).to eq 'user'
|
||||
expect(executor.server.chef_node_name).to eq 'node_name'
|
||||
expect(executor.server.key).to eq @key
|
||||
expect(executor.server.run_list).to eq @run_list
|
||||
end
|
||||
|
||||
it 'sets run list to an empty array by default' do
|
||||
@run_list = nil
|
||||
create_server
|
||||
expect(executor.server.run_list).to eq []
|
||||
end
|
||||
|
||||
it 'sets key to default provider ssh key by default' do
|
||||
@key = nil
|
||||
allow(provider).to receive(:ssh_key) { 'default_key' }
|
||||
create_server
|
||||
expect(executor.server.key).to eq 'default_key'
|
||||
end
|
||||
|
||||
it 'runs hooks' do
|
||||
expect(executor).to receive(:run_hook).with(:before_create).ordered
|
||||
expect(executor).to receive(:run_hook).with(:after_create).ordered
|
||||
create_server
|
||||
end
|
||||
|
||||
it 'creates server in cloud' do
|
||||
expect(provider).to receive(:create_server).with(
|
||||
an_instance_of(Devops::Model::Server), deploy_env.image, deploy_env.flavor, deploy_env.subnets, deploy_env.groups, output
|
||||
)
|
||||
create_server
|
||||
end
|
||||
|
||||
it 'inserts built server into mongo' do
|
||||
expect(stubbed_connector).to receive(:server_insert)
|
||||
create_server
|
||||
end
|
||||
|
||||
it 'schedules expiration for server' do
|
||||
deploy_env.expires = '2m'
|
||||
allow(DeleteServerWorker).to receive(:perform_in)
|
||||
expect(DeleteServerWorker).to receive(:perform_in).with(120, {server_chef_node_name: 'node_name'})
|
||||
create_server
|
||||
end
|
||||
|
||||
it "doesn't schedule expiration if deploy_env.expires is nil" do
|
||||
deploy_env.expires = nil
|
||||
expect(DeleteServerWorker).not_to receive(:perform_in)
|
||||
create_server
|
||||
end
|
||||
|
||||
context 'without_bootstrap option is false' do
|
||||
it 'launches bootstrap' do
|
||||
@without_bootstrap = false
|
||||
allow(image).to receive(:bootstrap_template) { 'template' }
|
||||
allow(executor).to receive(:two_phase_bootstrap)
|
||||
expect(executor).to receive(:two_phase_bootstrap)
|
||||
create_server
|
||||
end
|
||||
end
|
||||
|
||||
context 'without_bootstrap option is nil' do
|
||||
it 'launches bootstrap' do
|
||||
@without_bootstrap = nil
|
||||
allow(image).to receive(:bootstrap_template) { 'template' }
|
||||
allow(executor).to receive(:two_phase_bootstrap)
|
||||
expect(executor).to receive(:two_phase_bootstrap)
|
||||
create_server
|
||||
end
|
||||
end
|
||||
|
||||
context 'without_bootstrap option is true' do
|
||||
it "doesn't launch bootstrap" do
|
||||
@without_bootstrap = true
|
||||
expect(executor).not_to receive(:two_phase_bootstrap)
|
||||
create_server
|
||||
end
|
||||
end
|
||||
|
||||
context 'if error has been raised during execution' do
|
||||
before do
|
||||
allow(stubbed_connector).to receive(:server_delete)
|
||||
allow(provider).to receive(:create_server) { raise }
|
||||
end
|
||||
|
||||
it 'rollbacks server creating' do
|
||||
expect(executor).to receive(:roll_back)
|
||||
create_server
|
||||
end
|
||||
|
||||
it 'deletes server from mongo' do
|
||||
expect(stubbed_connector).to receive(:server_delete)
|
||||
create_server
|
||||
end
|
||||
end
|
||||
|
||||
context "if creating server in cloud wasn't successful" do
|
||||
it 'returns creating_server_in_cloud_failed error code' do
|
||||
allow(provider).to receive(:create_server) { false }
|
||||
expect(create_server).to eq 10
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
describe '#bootstrap', stubbed_knife: true do
|
||||
let(:image) { double('Key instance', path: 'path') }
|
||||
let(:bootstrap) { executor.bootstrap({}) }
|
||||
|
||||
before do
|
||||
allow(executor).to receive(:sleep)
|
||||
allow(executor).to receive(:last_command_successful?).and_return(true)
|
||||
allow(executor).to receive(:execute_system_command)
|
||||
allow(provider).to receive(:create_default_chef_node_name).and_return('chef_node')
|
||||
allow(stubbed_connector).to receive(:key).and_return(image)
|
||||
allow(stubbed_connector).to receive(:server_set_chef_node_name)
|
||||
allow(stubbed_knife).to receive(:knife_bootstrap).and_return(0)
|
||||
end
|
||||
|
||||
it 'run before hook' do
|
||||
expect(executor).to receive(:run_hook).with(:before_bootstrap, output).ordered
|
||||
expect(executor).to receive(:run_hook).with(:after_bootstrap, output).ordered
|
||||
bootstrap
|
||||
end
|
||||
|
||||
context "when server's private ip is unset" do
|
||||
it 'returns server_bootstrap_private_ip_unset error code' do
|
||||
server.private_ip = nil
|
||||
expect(bootstrap).to eq 4
|
||||
end
|
||||
end
|
||||
|
||||
it 'tries to ssh to server' do
|
||||
expect(executor).to receive(:execute_system_command).with(/ssh/)
|
||||
bootstrap
|
||||
end
|
||||
|
||||
context "couldn't ssh to server" do
|
||||
before { allow(executor).to receive(:last_command_successful?) { false } }
|
||||
|
||||
it 'tries to ssh to server maximum MAX_SSH_RETRIES_AMOUNT times' do
|
||||
max_retries = Devops::Executor::ServerExecutor::MAX_SSH_RETRIES_AMOUNT
|
||||
expect(executor).to receive(:execute_system_command).exactly(max_retries).times
|
||||
bootstrap
|
||||
end
|
||||
|
||||
it 'returns server_bootstrap_fail error code' do
|
||||
expect(bootstrap).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context 'after successful ssh check' do
|
||||
before { allow(executor).to receive(:last_command_successful?).and_return(false, true) }
|
||||
|
||||
it "sets default chef node name if it's nil" do
|
||||
executor.server.chef_node_name = nil
|
||||
expect {bootstrap}.to change {executor.server.chef_node_name}.to 'chef_node'
|
||||
end
|
||||
|
||||
it 'executes knife bootstrap' do
|
||||
expect(stubbed_knife).to receive(:knife_bootstrap).with(output, server.private_ip, instance_of(Array))
|
||||
bootstrap
|
||||
end
|
||||
|
||||
it "bootstraps to public ip if it's set" do
|
||||
server.public_ip = '8.8.8.8'
|
||||
expect(stubbed_knife).to receive(:knife_bootstrap).with(output, '8.8.8.8', instance_of(Array))
|
||||
bootstrap
|
||||
end
|
||||
|
||||
context 'after successful bootstrap' do
|
||||
it "updates server's chef node name in db" do
|
||||
expect(stubbed_connector).to receive(:server_set_chef_node_name).with(instance_of(Devops::Model::Server))
|
||||
bootstrap
|
||||
end
|
||||
end
|
||||
|
||||
context "if bootstraping wasn't successful" do
|
||||
before { allow(stubbed_knife).to receive(:knife_bootstrap).and_return(123) }
|
||||
|
||||
it 'returns :server_bootstrap_fail code' do
|
||||
expect(bootstrap).to eq 2
|
||||
end
|
||||
|
||||
it "doesn't run after hook" do
|
||||
expect(executor).to receive(:run_hook).with(:before_bootstrap, output)
|
||||
bootstrap
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
describe '#two_phase_bootstrap', stubbed_knife: true do
|
||||
let(:two_phase_bootstrap) { executor.two_phase_bootstrap({}) }
|
||||
|
||||
before do
|
||||
allow(provider).to receive(:run_list) {[]}
|
||||
allow(stubbed_connector).to receive(:server_delete)
|
||||
end
|
||||
|
||||
context 'when bootstrap was successful' do
|
||||
before do
|
||||
allow(executor).to receive(:bootstrap) { 0 }
|
||||
allow(executor).to receive(:check_server_on_chef_server) { false }
|
||||
end
|
||||
|
||||
context 'if node presents on chef server' do
|
||||
before do
|
||||
allow(executor).to receive(:check_server_on_chef_server) { true }
|
||||
allow(executor).to receive(:deploy_server)
|
||||
allow(stubbed_knife).to receive(:set_run_list)
|
||||
end
|
||||
|
||||
it 'builds run list' do
|
||||
expect(executor).to receive(:compute_run_list)
|
||||
two_phase_bootstrap
|
||||
end
|
||||
|
||||
it 'sets run list to chef node' do
|
||||
expect(stubbed_knife).to receive(:set_run_list)
|
||||
two_phase_bootstrap
|
||||
end
|
||||
|
||||
it 'deploys server' do
|
||||
expect(executor).to receive(:deploy_server)
|
||||
two_phase_bootstrap
|
||||
end
|
||||
|
||||
context 'if deploy was successful' do
|
||||
it 'returns 0' do
|
||||
allow(executor).to receive(:deploy_server) { 0 }
|
||||
expect(two_phase_bootstrap).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "if deploy wasn't successful" do
|
||||
it 'returns :deploy_failed code' do
|
||||
allow(executor).to receive(:deploy_server) { 1 }
|
||||
expect(two_phase_bootstrap).to eq 8
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an error occured during deploy' do
|
||||
it 'returns :deploy_unknown_error code' do
|
||||
allow(executor).to receive(:deploy_server) { raise }
|
||||
expect(two_phase_bootstrap).to eq 6
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "if node doesn't present on chef server" do
|
||||
it 'roll backs and then deletes server from mongo' do
|
||||
allow(executor).to receive(:check_server_on_chef_server) { false }
|
||||
allow(executor).to receive(:roll_back)
|
||||
allow(stubbed_connector).to receive(:server_delete)
|
||||
expect(executor).to receive(:roll_back).ordered
|
||||
expect(stubbed_connector).to receive(:server_delete).ordered
|
||||
two_phase_bootstrap
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when bootstrap wasn't successful" do
|
||||
it 'returns :server_bootstrap_fail error code' do
|
||||
allow(executor).to receive(:bootstrap) { 1 }
|
||||
expect(two_phase_bootstrap).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an error occured during bootstrap' do
|
||||
it 'returns :server_bootstrap_unknown_error error code' do
|
||||
allow(executor).to receive(:bootstrap) { raise }
|
||||
expect(two_phase_bootstrap).to eq 7
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#check_server_on_chef_server', stubbed_knife: true do
|
||||
before do
|
||||
server.chef_node_name = 'a'
|
||||
allow(stubbed_knife).to receive(:chef_node_list) { @node_list }
|
||||
allow(stubbed_knife).to receive(:chef_client_list) { @client_list }
|
||||
end
|
||||
|
||||
it 'returns true when node_name in node list and in client list' do
|
||||
@node_list = %w(a); @client_list = %w(a)
|
||||
expect(executor.check_server_on_chef_server).to be true
|
||||
end
|
||||
|
||||
it "returns false if node name isn't in node list" do
|
||||
@node_list = []; @client_list = %w(a)
|
||||
expect(executor.check_server_on_chef_server).to be false
|
||||
end
|
||||
|
||||
it "returns false if node name isn't in node list" do
|
||||
@node_list = %w(a); @client_list = []
|
||||
expect(executor.check_server_on_chef_server).to be false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#unbootstrap', stubbed_knife: true do
|
||||
before do
|
||||
allow(stubbed_connector).to receive_message_chain('key.path') { 'path_to_key' }
|
||||
allow(stubbed_knife).to receive(:chef_node_delete)
|
||||
allow(stubbed_knife).to receive(:chef_client_delete)
|
||||
allow(executor).to receive(:execute_system_command) { '' }
|
||||
allow(executor).to receive(:last_command_successful?) { true }
|
||||
allow(executor).to receive(:sleep)
|
||||
allow(Net::SSH).to receive(:start)
|
||||
end
|
||||
|
||||
it 'connects by ssh' do
|
||||
expect(Net::SSH).to receive(:start)
|
||||
executor.unbootstrap
|
||||
end
|
||||
|
||||
it 'returns hash with error after 5 unsuccessful retries' do
|
||||
allow(Net::SSH).to receive(:start) { raise }
|
||||
expect(Net::SSH).to receive(:start).exactly(5).times
|
||||
expect(executor.unbootstrap).to be_a(Hash).and include(:error)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#deploy_server_with_tags', stubbed_knife: true do
|
||||
let(:current_tags) { @current_tags }
|
||||
let(:initial_tags) { %w(a b) }
|
||||
let(:joined_initial_tags) { initial_tags.join(' ') }
|
||||
let(:given_tags) { %w(c d) }
|
||||
let(:joined_given_tags) { given_tags.join(' ') }
|
||||
let(:deploy_server_with_tags) { executor.deploy_server_with_tags(given_tags, {}) }
|
||||
|
||||
before do
|
||||
@current_tags = initial_tags.dup
|
||||
allow(stubbed_knife).to receive(:tags_list) { @current_tags }
|
||||
allow(stubbed_knife).to receive(:swap_tags) do |_, tags_to_delete, tags_to_add|
|
||||
@current_tags -= tags_to_delete.split
|
||||
@current_tags += tags_to_add.split
|
||||
end
|
||||
allow(executor).to receive(:deploy_server)
|
||||
end
|
||||
|
||||
context 'when tags are empty' do
|
||||
it 'just deploys server' do
|
||||
expect(executor).to receive(:deploy_server)
|
||||
expect(stubbed_knife).not_to receive(:swap_tags)
|
||||
executor.deploy_server_with_tags([], {})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when tags are not empty' do
|
||||
it 'temporarily swaps current_tags with given ones, deploys server and then restores tags' do
|
||||
expect(stubbed_knife).to receive(:tags_list).ordered
|
||||
expect(stubbed_knife).to receive(:swap_tags).with(instance_of(String), joined_initial_tags, joined_given_tags).ordered
|
||||
expect(executor).to receive(:deploy_server).ordered
|
||||
expect(stubbed_knife).to receive(:swap_tags).with(instance_of(String), joined_given_tags, joined_initial_tags).ordered
|
||||
deploy_server_with_tags
|
||||
end
|
||||
end
|
||||
|
||||
context 'if error occures during deploy' do
|
||||
it 'restores tags anyway' do
|
||||
allow(executor).to receive(:deploy_server) { raise }
|
||||
expect {
|
||||
deploy_server_with_tags
|
||||
}.to raise_error StandardError
|
||||
expect(current_tags).to eq initial_tags
|
||||
end
|
||||
end
|
||||
|
||||
context 'if cannot add tags to server' do
|
||||
it 'returns :server_cannot_update_tags code' do
|
||||
allow(stubbed_knife).to receive(:swap_tags) { false }
|
||||
expect(deploy_server_with_tags).to eq 3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
describe '#deploy_server', stubbed_knife: true do
|
||||
let(:deploy_info) { @deploy_info }
|
||||
let(:json_file_name) { 'json.json' }
|
||||
let(:json_file_path) { File.join(SpecSupport.tmp_dir, json_file_name) }
|
||||
|
||||
let(:deploy_server) { executor.deploy_server(deploy_info) }
|
||||
|
||||
before do
|
||||
allow(executor).to receive(:run_hook).with(:before_deploy, any_args)
|
||||
allow(executor).to receive(:run_hook).with(:after_deploy, any_args)
|
||||
allow(stubbed_knife).to receive(:ssh_stream) { 'Chef Client finished'}
|
||||
allow(stubbed_connector).to receive(:key) { double('Key', path: 'path_to_key') }
|
||||
allow(stubbed_connector).to receive(:server_update)
|
||||
@deploy_info = {}
|
||||
end
|
||||
|
||||
|
||||
it 'runs before_deploy and after_deploy hooks' do
|
||||
expect(executor).to receive(:run_hook).with(:before_deploy, any_args).ordered
|
||||
expect(executor).to receive(:run_hook).with(:after_deploy, any_args).ordered
|
||||
deploy_server
|
||||
end
|
||||
|
||||
context 'when uses json file' do
|
||||
before(:all) do
|
||||
@tmp_files_at_start = Dir.entries(SpecSupport.tmp_dir)
|
||||
end
|
||||
before do
|
||||
allow(DevopsConfig).to receive(:config).and_return({
|
||||
project_info_dir: SpecSupport.tmp_dir,
|
||||
address: 'host.com',
|
||||
port: '8080',
|
||||
url_prefix: 'api'
|
||||
})
|
||||
deploy_info['use_json_file'] = true
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
diff = Dir.entries(SpecSupport.tmp_dir) - @tmp_files_at_start
|
||||
diff.each do |file|
|
||||
FileUtils.rm(File.join(SpecSupport.tmp_dir, file))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
it 'writes deploy_info to json file if it not exists' do
|
||||
expect { deploy_server }.to change { Dir.entries(SpecSupport.tmp_dir)}
|
||||
end
|
||||
|
||||
it "writes deploy_info to given json file name if it doesn't exist" do
|
||||
FileUtils.rm(json_file_path) if File.exists?(json_file_path)
|
||||
deploy_info['json_file'] = json_file_name
|
||||
expect { deploy_server }.to change {
|
||||
Dir.entries(SpecSupport.tmp_dir)
|
||||
}
|
||||
FileUtils.rm(json_file_path)
|
||||
end
|
||||
|
||||
it 'reads json from file if it exists' do
|
||||
deploy_info['json_file'] = json_file_name
|
||||
File.open(json_file_path, 'w') { |file| file.puts '{"foo": "bar"'}
|
||||
expect { deploy_server }.not_to change {
|
||||
Dir.entries(SpecSupport.tmp_dir)
|
||||
}
|
||||
FileUtils.rm(json_file_path)
|
||||
end
|
||||
|
||||
it 'adds link to json to deploy command' do
|
||||
deploy_info['json_file'] = json_file_name
|
||||
regexp = %r(-j http://host.com:8080/api/v2.0/deploy/data/#{json_file_name})
|
||||
expect(stubbed_knife).to receive(:ssh_stream).with(anything, regexp, any_args)
|
||||
deploy_server
|
||||
end
|
||||
end
|
||||
|
||||
context "doesn't use json file" do
|
||||
before do
|
||||
deploy_info['use_json_file'] = false
|
||||
deploy_info['run_list'] = %w(foo bar)
|
||||
end
|
||||
|
||||
it "adds run list to command if server's stack is set" do
|
||||
server.stack = 'stack'
|
||||
expect(stubbed_knife).to receive(:ssh_stream).with(anything, %r(-r foo,bar), any_args)
|
||||
deploy_server
|
||||
end
|
||||
|
||||
it "doesn't add run list to command if server's stack is unset" do
|
||||
expect(stubbed_knife).to receive(:ssh_stream).with(anything, 'chef-client --no-color', any_args)
|
||||
deploy_server
|
||||
end
|
||||
end
|
||||
|
||||
it "uses server's key" do
|
||||
expect(stubbed_connector).to receive(:key).with('key_id')
|
||||
expect(stubbed_knife).to receive(:ssh_stream).with(any_args, 'path_to_key')
|
||||
deploy_server
|
||||
end
|
||||
|
||||
it "uses public ip if it's set" do
|
||||
server.public_ip = '127.0.0.1'
|
||||
expect(stubbed_knife).to receive(:ssh_stream).with(anything, anything, '127.0.0.1', any_args)
|
||||
deploy_server
|
||||
end
|
||||
|
||||
it "uses private_ip if public_ip isn't set" do
|
||||
expect(stubbed_knife).to receive(:ssh_stream).with(anything, anything, server.private_ip, any_args)
|
||||
deploy_server
|
||||
end
|
||||
|
||||
context 'if deploy was successful' do
|
||||
it "updates server's last operation" do
|
||||
expect(server).to receive(:set_last_operation).with('deploy', anything)
|
||||
expect(stubbed_connector).to receive(:server_update).with(server)
|
||||
deploy_server
|
||||
end
|
||||
|
||||
it 'returns 0' do
|
||||
expect(deploy_server).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context "when deploy wasn't successful" do
|
||||
before { allow(stubbed_knife).to receive(:ssh_stream) { 'fail'} }
|
||||
it "doesn't run after_deploy hook" do
|
||||
expect(executor).to receive(:run_hook).with(:before_deploy, any_args)
|
||||
expect(executor).not_to receive(:run_hook).with(:after_deploy, any_args)
|
||||
deploy_server
|
||||
end
|
||||
|
||||
it 'returns 1' do
|
||||
expect(deploy_server).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#delete_from_chef_server', stubbed_knife: true do
|
||||
let(:delete_from_chef_server) { executor.delete_from_chef_server('foo') }
|
||||
before do
|
||||
allow(stubbed_knife).to receive(:chef_client_delete)
|
||||
allow(stubbed_knife).to receive(:chef_node_delete)
|
||||
delete_from_chef_server
|
||||
end
|
||||
|
||||
it 'returns hash with :chef_node and :chef_client keys' do
|
||||
expect(delete_from_chef_server).to be_a(Hash).and include(:chef_node, :chef_client)
|
||||
end
|
||||
|
||||
it 'calls to :chef_node_delete and :chef_client_delete' do
|
||||
expect(stubbed_knife).to have_received(:chef_client_delete)
|
||||
expect(stubbed_knife).to have_received(:chef_node_delete)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#delete_server' do
|
||||
let(:delete_server) { executor.delete_server }
|
||||
|
||||
context 'when server is static' do
|
||||
before do
|
||||
server.provider = 'static'
|
||||
allow(stubbed_connector).to receive(:server_delete).with(server.id)
|
||||
allow(executor).to receive(:unbootstrap)
|
||||
end
|
||||
|
||||
it 'performs unbootstrap' do
|
||||
expect(executor).to receive(:unbootstrap)
|
||||
delete_server
|
||||
end
|
||||
|
||||
it 'deletes server from mongo' do
|
||||
expect(stubbed_connector).to receive(:server_delete).with(server.id)
|
||||
delete_server
|
||||
end
|
||||
|
||||
it 'returns message and nil' do
|
||||
expect(delete_server.first).to be_a(String)
|
||||
expect(delete_server.last).to be nil
|
||||
end
|
||||
|
||||
it "doesn't try to remove it from cloud" do
|
||||
expect{delete_server}.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context "when server isn't static", stubbed_knife: true do
|
||||
before do
|
||||
allow(server).to receive_message_chain('provider_instance.delete_server')
|
||||
allow(stubbed_connector).to receive(:server_delete).with(server.id)
|
||||
allow(stubbed_knife).to receive(:chef_node_delete)
|
||||
allow(stubbed_knife).to receive(:chef_client_delete)
|
||||
end
|
||||
|
||||
it 'deletes from info about note chef server' do
|
||||
allow(executor).to receive(:delete_from_chef_server).and_call_original
|
||||
expect(executor).to receive(:delete_from_chef_server)
|
||||
delete_server
|
||||
end
|
||||
|
||||
it "doesn't unbootstrap server" do
|
||||
expect(executor).not_to receive(:unbootstrap)
|
||||
delete_server
|
||||
end
|
||||
|
||||
it 'deletes server from cloud' do
|
||||
expect(server).to receive_message_chain('provider_instance.delete_server').with(server)
|
||||
delete_server
|
||||
end
|
||||
|
||||
it "doesn't raise error if server wasn't found in cloud" do
|
||||
allow(server).to receive_message_chain('provider_instance.name')
|
||||
allow(server).to receive_message_chain('provider_instance.delete_server') {
|
||||
raise Fog::Compute::OpenStack::NotFound
|
||||
}
|
||||
expect { delete_server }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'deletes server from mongo' do
|
||||
expect(stubbed_connector).to receive(:server_delete).with(server.id)
|
||||
delete_server
|
||||
end
|
||||
|
||||
it 'returns message and hash with :chef_node, :chef_client and :server keys' do
|
||||
expect(delete_server.first).to be_a(String)
|
||||
expect(delete_server.last).to be_a(Hash).and include(:chef_client, :chef_node, :server)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#rollback' do
|
||||
before do
|
||||
allow(executor).to receive(:delete_from_chef_server) { {} }
|
||||
allow(server).to receive_message_chain('provider_instance.delete_server')
|
||||
end
|
||||
|
||||
it "does nothing if server.id is nil" do
|
||||
server.id = nil
|
||||
expect(executor).not_to receive(:delete_from_chef_server)
|
||||
expect(server).not_to receive(:provider_instance)
|
||||
executor.roll_back
|
||||
end
|
||||
|
||||
it 'deletes node from chef server and instance from cloud' do
|
||||
expect(executor).to receive(:delete_from_chef_server)
|
||||
expect(server).to receive_message_chain('provider_instance.delete_server')
|
||||
executor.roll_back
|
||||
end
|
||||
|
||||
it "doesn't raise if deleting server in cloud raises an error" do
|
||||
allow(server).to receive_message_chain('provider_instance.delete_server') { raise }
|
||||
expect { executor.roll_back }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe '#add_run_list_to_deploy_info' do
|
||||
it "doesn't change deploy info if it already includes run list" do
|
||||
deploy_info = {'run_list' => %w(foo)}
|
||||
expect {
|
||||
executor.add_run_list_to_deploy_info(output, deploy_info)
|
||||
}.not_to change { deploy_info }
|
||||
end
|
||||
|
||||
it 'computes and adds run_list to deploy_info' do
|
||||
deploy_info = {}
|
||||
allow(executor).to receive(:compute_run_list) { %w(foo) }
|
||||
expect(executor).to receive(:compute_run_list)
|
||||
executor.add_run_list_to_deploy_info(output, deploy_info)
|
||||
expect(deploy_info['run_list']).to eq %w(foo)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#compute_run_list' do
|
||||
before do
|
||||
allow(deploy_env).to receive_message_chain('provider_instance.run_list') { %w(a) }
|
||||
project.run_list = %w(b)
|
||||
deploy_env.run_list = %w(c)
|
||||
server.run_list = %w(d)
|
||||
end
|
||||
|
||||
it "returns array with run list merged from provider's, project's, env's and server's run lists" do
|
||||
expect(executor.compute_run_list).to be_an(Array).and contain_exactly(*%w(a b c d))
|
||||
end
|
||||
|
||||
it "includes stack's run list if stack is set", stubbed_connector: true do
|
||||
server.stack = 'stack'
|
||||
allow(stubbed_connector).to receive(:stack) { instance_double(Devops::Model::StackEc2, run_list: %w(e)) }
|
||||
expect(executor.compute_run_list).to be_an(Array).and contain_exactly(*%w(a b c d e))
|
||||
end
|
||||
|
||||
it "doesn't contain nils" do
|
||||
server.run_list = nil
|
||||
server.stack = 'stack'
|
||||
allow(stubbed_connector).to receive(:stack) { instance_double(Devops::Model::StackEc2, run_list: nil) }
|
||||
expect(executor.compute_run_list).to be_an(Array).and contain_exactly(*%w(a b c))
|
||||
end
|
||||
|
||||
it 'returns uniq elements' do
|
||||
project.run_list = %w(a)
|
||||
deploy_env.run_list = %w(a)
|
||||
expect(executor.compute_run_list).to be_an(Array).and contain_exactly(*%w(a d))
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -3,7 +3,7 @@ require 'db/mongo/models/key'
|
||||
FactoryGirl.define do
|
||||
factory :key, class: Devops::Model::Key do
|
||||
id 'user_key'
|
||||
path SpecSupport::BLANK_FILE
|
||||
path SpecSupport.blank_file
|
||||
scope 'user'
|
||||
end
|
||||
end
|
||||
@ -5,7 +5,21 @@ require_relative 'shared_cloud_deploy_env_specs'
|
||||
RSpec.describe Devops::Model::DeployEnvEc2, type: :model do
|
||||
let(:env) { build(:deploy_env_ec2) }
|
||||
|
||||
describe 'it inherits from cloud deploy_env', stubbed_env_validators: true, stubbed_logger: true do
|
||||
describe 'it inherits from cloud deploy_env', stubbed_connector: true, stubbed_logger: true do
|
||||
before do
|
||||
provider_double = instance_double('Provider::Ec2',
|
||||
flavors: [{'id' => 'flavor'}],
|
||||
networks: [{'default' => {'vpcId' => 'foo'}}],
|
||||
groups: {'default' => nil},
|
||||
images: [{'id' => 'image'}]
|
||||
)
|
||||
allow(Provider::ProviderFactory).to receive(:providers) { %w(ec2) }
|
||||
allow(Provider::ProviderFactory).to receive(:get) { provider_double }
|
||||
allow(stubbed_connector).to receive(:users_names) { %w(root) }
|
||||
allow(stubbed_connector).to receive(:available_images) { %w(image) }
|
||||
allow(stubbed_connector).to receive(:stack_templates) { [build(:stack_template_ec2, id: 'template')] }
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy env'
|
||||
it_behaves_like 'cloud deploy env'
|
||||
end
|
||||
|
||||
@ -5,7 +5,21 @@ require_relative 'shared_cloud_deploy_env_specs'
|
||||
RSpec.describe Devops::Model::DeployEnvOpenstack, type: :model do
|
||||
let(:env) { build(:deploy_env_openstack) }
|
||||
|
||||
describe 'it inherits from cloud deploy_env', stubbed_env_validators: true, stubbed_logger: true do
|
||||
describe 'it inherits from cloud deploy_env', stubbed_connector: true, stubbed_logger: true do
|
||||
before do
|
||||
provider_double = instance_double('Provider::Openstack',
|
||||
flavors: [{'id' => 'flavor'}],
|
||||
networks: [{'default' => {'vpcId' => 'foo'}}],
|
||||
groups: {'default' => nil},
|
||||
images: [{'id' => 'image'}]
|
||||
)
|
||||
allow(Provider::ProviderFactory).to receive(:providers) { %w(openstack) }
|
||||
allow(Provider::ProviderFactory).to receive(:get) { provider_double }
|
||||
allow(stubbed_connector).to receive(:users_names) { %w(root) }
|
||||
allow(stubbed_connector).to receive(:available_images) { %w(image) }
|
||||
allow(stubbed_connector).to receive(:stack_templates) { [build(:stack_template_openstack, id: 'template')] }
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy env'
|
||||
it_behaves_like 'cloud deploy env'
|
||||
end
|
||||
|
||||
@ -5,10 +5,10 @@ RSpec.describe Devops::Model::DeployEnvStatic, type: :model do
|
||||
let(:env) { build(:deploy_env_static) }
|
||||
|
||||
|
||||
describe 'it inherits from deploy env', stubbed_logger: true do
|
||||
describe 'it inherits from deploy env', stubbed_logger: true, stubbed_connector: true do
|
||||
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'])
|
||||
allow(stubbed_connector).to receive(:users_names) { %w(root) }
|
||||
end
|
||||
|
||||
it_behaves_like 'deploy env'
|
||||
|
||||
@ -3,7 +3,21 @@ require 'db/mongo/models/project'
|
||||
RSpec.describe Devops::Model::Project, type: :model do
|
||||
let(:project) { build(:project) }
|
||||
|
||||
describe 'validation rules:', stubbed_env_validators: true, stubbed_logger: true do
|
||||
describe 'validation rules:', stubbed_connector: true, stubbed_logger: true do
|
||||
before do
|
||||
provider_double = instance_double('Provider::Ec2',
|
||||
flavors: [{'id' => 'flavor'}],
|
||||
networks: [{'default' => {'vpcId' => 'foo'}}],
|
||||
groups: {'default' => nil},
|
||||
images: [{'id' => 'image'}]
|
||||
)
|
||||
allow(Provider::ProviderFactory).to receive(:providers) { %w(ec2) }
|
||||
allow(Provider::ProviderFactory).to receive(:get) { provider_double }
|
||||
allow(stubbed_connector).to receive(:users_names) { %w(root) }
|
||||
allow(stubbed_connector).to receive(:available_images) { %w(image) }
|
||||
allow(stubbed_connector).to receive(:stack_templates) { [build(:stack_template_ec2, id: 'template')] }
|
||||
end
|
||||
|
||||
include_examples 'field type validation', :id, :not_nil, :non_empty_string
|
||||
include_examples 'field type validation', :deploy_envs, :not_nil, :non_empty_array
|
||||
include_examples 'field type validation', :description, :maybe_nil, :maybe_empty_string
|
||||
@ -23,6 +37,23 @@ RSpec.describe Devops::Model::Project, type: :model do
|
||||
project = build(:project, with_deploy_env_identifiers: ['foo', nil])
|
||||
expect(project).not_to be_valid
|
||||
end
|
||||
|
||||
describe 'components validation' do
|
||||
it 'is valid with components with filenames' do
|
||||
project.components = {'foo' => {'filename' => 'bar'}}
|
||||
expect{project.validate_components}.not_to raise_error
|
||||
end
|
||||
|
||||
it "isn't valid if components isn't a hash" do
|
||||
project.components = []
|
||||
expect{project.validate_components}.to raise_error InvalidRecord
|
||||
end
|
||||
|
||||
it "raises InvalidRecord if one of componentsц hasn't filename" do
|
||||
project.components = {'foo' => {}}
|
||||
expect{project.validate_components}.to raise_error InvalidRecord
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.fields' do
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
require 'spec_helper'
|
||||
|
||||
# не пытайся выделить в shared_specs, фигня выйдет
|
||||
RSpec.describe Devops::Model::OpenstackProviderAccount, type: :model do
|
||||
let(:provider_account) { build(:openstack_provider_account) }
|
||||
|
||||
|
||||
@ -44,6 +44,14 @@ RSpec.describe Devops::Model::Server, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.build_from_bson' do
|
||||
it 'takes a hash and returns instance of Server model' do
|
||||
model = described_class.build_from_bson('id' => 'foo')
|
||||
expect(model).to be_an_instance_of(described_class)
|
||||
expect(model.id).to eq 'foo'
|
||||
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))
|
||||
|
||||
@ -6,21 +6,23 @@ RSpec.describe Devops::Model::StackTemplateEc2, type: :model do
|
||||
|
||||
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 }
|
||||
allow_any_instance_of(Devops::Model::StackTemplateEc2).to receive_message_chain('provider_instance.store_stack_template') { {'url' => nil} }
|
||||
provider_double = instance_double('Provider::Ec2',
|
||||
validate_stack_template: true,
|
||||
store_stack_template: {'url' => 'template_url'}
|
||||
)
|
||||
allow(Provider::ProviderFactory).to receive(:get) { provider_double }
|
||||
end
|
||||
|
||||
it_behaves_like 'stack template'
|
||||
|
||||
it 'uploads file to S3' do
|
||||
expect_any_instance_of(Devops::Model::StackTemplateEc2).to receive_message_chain('provider_instance.store_stack_template')
|
||||
params = {
|
||||
'id' => 'foo',
|
||||
result = described_class.create('id' => 'foo',
|
||||
'template_body' => '{}',
|
||||
'owner' => 'root',
|
||||
'provider' => 'ec2'
|
||||
}
|
||||
expect(described_class.create(params)).to be_an_instance_of(described_class)
|
||||
)
|
||||
expect(result).to be_an_instance_of(described_class)
|
||||
expect(result.template_url).to eq 'template_url'
|
||||
end
|
||||
|
||||
end
|
||||
@ -79,13 +79,25 @@ RSpec.describe Devops::Model::User, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.build_from_bson' do
|
||||
it 'builds User model from given hash and assigns id' do
|
||||
model = described_class.build_from_bson('_id' => 'foo', 'username' => 'not shown', 'email' => 'baz')
|
||||
expect(model.id).to eq 'foo'
|
||||
expect(model.email).to eq 'baz'
|
||||
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')
|
||||
expect{user.check_privileges('key', 'r')}.not_to raise_error
|
||||
end
|
||||
|
||||
it 'raises InvalidPrivileges if given privelege is wrong' do
|
||||
expect{user.check_privileges('key', 't')}.to raise_error InvalidPrivileges
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
RSpec.shared_context 'stubbed calls to connector', stubbed_connector: true do
|
||||
let(:stubbed_connector) { double() }
|
||||
let(:stubbed_connector) { instance_double(MongoConnector) }
|
||||
before do
|
||||
allow(Devops::Db).to receive(:connector) { stubbed_connector }
|
||||
end
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
RSpec.shared_context 'stubbed calls to connector in env validators', stubbed_env_validators: true do
|
||||
before do
|
||||
allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(ec2 openstack))
|
||||
allow_any_instance_of(env_class).to receive_message_chain('provider_instance.flavors').and_return [{'id' => 'flavor'}]
|
||||
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::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'}])
|
||||
|
||||
6
devops-service/spec/shared_contexts/stubbed_knife.rb
Normal file
6
devops-service/spec/shared_contexts/stubbed_knife.rb
Normal file
@ -0,0 +1,6 @@
|
||||
RSpec.shared_context 'stubbed calls to KnifeFactory.instance', stubbed_knife: true do
|
||||
let(:stubbed_knife) { instance_double(KnifeCommands) }
|
||||
before do
|
||||
allow(KnifeFactory).to receive(:instance) { stubbed_knife }
|
||||
end
|
||||
end
|
||||
@ -3,5 +3,6 @@ RSpec.shared_context 'stubbed calls to logger', stubbed_logger: true do
|
||||
allow(DevopsLogger).to receive_message_chain('logger.debug')
|
||||
allow(DevopsLogger).to receive_message_chain('logger.info')
|
||||
allow(DevopsLogger).to receive_message_chain('logger.error')
|
||||
allow(DevopsLogger).to receive_message_chain('logger.warn')
|
||||
end
|
||||
end
|
||||
@ -4,32 +4,53 @@ 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
|
||||
|
||||
# suppress output
|
||||
original_stdout = $stdout
|
||||
$stdout = File.open(File::NULL, "w")
|
||||
|
||||
Dir[("./spec/support/**/*.rb")].each { |f| require f }
|
||||
Dir[("./spec/shared_contexts/**/*.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! }
|
||||
def suppress_output!
|
||||
original_stdout = $stdout
|
||||
$stdout = File.open(File::NULL, "w")
|
||||
RSpec.configure do |config|
|
||||
config.after(:all) do
|
||||
$stdout = original_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
FactoryGirl.find_definitions
|
||||
|
||||
def check_coverage
|
||||
require 'simplecov'
|
||||
if ENV['JENKINS']
|
||||
require 'simplecov-rcov'
|
||||
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
||||
end
|
||||
SimpleCov.start do
|
||||
add_filter { |src| src.filename =~ /spec\// }
|
||||
end
|
||||
end
|
||||
|
||||
def require_support_files
|
||||
root = File.join(File.dirname(__FILE__), "..")
|
||||
$LOAD_PATH.push root unless $LOAD_PATH.include?(root)
|
||||
Dir[("#{root}/spec/support/**/*.rb")].each { |f| require f }
|
||||
Dir[("#{root}/spec/shared_contexts/**/*.rb")].each { |f| require f }
|
||||
end
|
||||
|
||||
def setup_factory_girl
|
||||
FactoryGirl.define do
|
||||
# do not persist, but raise validation errors
|
||||
to_create { |model| model.validate! }
|
||||
end
|
||||
FactoryGirl.find_definitions
|
||||
RSpec.configure { |config| config.include FactoryGirl::Syntax::Methods }
|
||||
end
|
||||
|
||||
|
||||
# extra configuration
|
||||
suppress_output!
|
||||
check_coverage if ENV['COVERAGE']
|
||||
require_support_files
|
||||
setup_factory_girl
|
||||
|
||||
|
||||
# RSpec configuration
|
||||
RSpec.configure do |config|
|
||||
config.include FactoryGirl::Syntax::Methods
|
||||
|
||||
config.after(:all) do
|
||||
$stdout = original_stdout
|
||||
end
|
||||
|
||||
# rspec-expectations config goes here. You can use an alternate
|
||||
# assertion/expectation library such as wrong or the stdlib/minitest
|
||||
# assertions if you prefer.
|
||||
|
||||
6
devops-service/spec/support/instance_variable_matcher.rb
Normal file
6
devops-service/spec/support/instance_variable_matcher.rb
Normal file
@ -0,0 +1,6 @@
|
||||
RSpec::Matchers.define :have_instance_variable_value do |name, value|
|
||||
match do |actual|
|
||||
actual.instance_variable_defined?("@#{name}") &&
|
||||
actual.instance_variable_get("@#{name}") == value
|
||||
end
|
||||
end
|
||||
@ -2,7 +2,15 @@ require 'yaml'
|
||||
|
||||
module SpecSupport
|
||||
ROOT = File.join(__dir__, '../../')
|
||||
BLANK_FILE = File.join(ROOT, 'spec/support/blank_file')
|
||||
|
||||
def self.blank_file
|
||||
File.join(ROOT, 'spec/support/templates/blank_file')
|
||||
end
|
||||
|
||||
# for specs which write files
|
||||
def self.tmp_dir
|
||||
File.join(ROOT, 'spec/support/tmp/')
|
||||
end
|
||||
|
||||
def self.db_params
|
||||
@db_params ||= begin
|
||||
@ -32,4 +40,8 @@ module SpecSupport
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.root
|
||||
File.join(__dir__, '../../')
|
||||
end
|
||||
end
|
||||
0
devops-service/spec/support/tmp/.gitkeep
Normal file
0
devops-service/spec/support/tmp/.gitkeep
Normal file
@ -1,6 +1,7 @@
|
||||
require "db/mongo/models/server"
|
||||
require "db/mongo/models/report"
|
||||
require "lib/executors/server_executor"
|
||||
require "workers/worker"
|
||||
|
||||
class DeleteServerWorker < Worker
|
||||
|
||||
|
||||
@ -130,7 +130,7 @@ class StackBootstrapWorker < Worker
|
||||
|
||||
@out.puts
|
||||
results.each do |chef_node_name, code|
|
||||
human_readable_code = Devops::Executor::ServerExecutor.symbolic_result_code(code)
|
||||
human_readable_code = Devops::Executor::ServerExecutor.symbolic_error_code(code)
|
||||
@out.puts "Operation result for #{chef_node_name}: #{human_readable_code}"
|
||||
end
|
||||
|
||||
@ -144,7 +144,7 @@ class StackBootstrapWorker < Worker
|
||||
def errors_in_bootstrapping_present?(result_codes)
|
||||
bootstrap_error_codes = []
|
||||
[:server_bootstrap_fail, :server_not_in_chef_nodes, :server_bootstrap_unknown_error].each do |symbolic_code|
|
||||
bootstrap_error_codes << Devops::Executor::ServerExecutor.result_code(symbolic_code)
|
||||
bootstrap_error_codes << Devops::Executor::ServerExecutor.error_code(symbolic_code)
|
||||
end
|
||||
|
||||
(bootstrap_error_codes & result_codes).size > 0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user