Merge branch 'features' of /home/git/repositories/cloudtechlab/devops-service into qa
This commit is contained in:
commit
b32b39545d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
devops-service/tests/features/support/config.yml
|
devops-service/tests/features/support/config.yml
|
||||||
.devops_files/
|
.devops_files/
|
||||||
devops-service/plugins
|
devops-service/plugins
|
||||||
|
devops-service/spec/examples.txt
|
||||||
@ -33,7 +33,7 @@ module DevopsClient
|
|||||||
if config[:host].nil?
|
if config[:host].nil?
|
||||||
abort(I18n.t("config.invalid.host"), :file => @@config_file)
|
abort(I18n.t("config.invalid.host"), :file => @@config_file)
|
||||||
end
|
end
|
||||||
[:api, :username, :password].each do |key|
|
[:api, :username].each do |key|
|
||||||
if config[key].nil? or config[key].empty?
|
if config[key].nil? or config[key].empty?
|
||||||
abort(I18n.t("config.invalid.empty", :file => @@config_file, :key => key))
|
abort(I18n.t("config.invalid.empty", :file => @@config_file, :key => key))
|
||||||
end
|
end
|
||||||
|
|||||||
@ -5,8 +5,8 @@ module Outputtable
|
|||||||
end
|
end
|
||||||
|
|
||||||
def outputter
|
def outputter
|
||||||
raise 'You should use "output_with" method to define outputter' unless defined?(outputter_class)
|
raise 'You should use "output_with" method to define outputter' unless self.class.outputter_class
|
||||||
@outputter ||= outputter_class.new(data_to_output, options.merge(current_command: current_command))
|
@outputter ||= self.class.outputter_class.new(data_to_output, options.merge(current_command: current_command))
|
||||||
end
|
end
|
||||||
|
|
||||||
def output(options={})
|
def output(options={})
|
||||||
@ -19,10 +19,9 @@ module Outputtable
|
|||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
attr_reader :outputter_class
|
||||||
def output_with(klass)
|
def output_with(klass)
|
||||||
define_method :outputter_class do
|
@outputter_class = klass
|
||||||
klass
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -84,6 +84,10 @@ class Project < Handler
|
|||||||
when "test"
|
when "test"
|
||||||
self.options = @options_parser.test_options
|
self.options = @options_parser.test_options
|
||||||
test_handler @options_parser.args
|
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
|
else
|
||||||
@options_parser.invalid_command
|
@options_parser.invalid_command
|
||||||
end
|
end
|
||||||
@ -440,4 +444,39 @@ protected
|
|||||||
@list || @show || @servers || @test
|
@list || @show || @servers || @test
|
||||||
end
|
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
|
||||||
|
|
||||||
|
ask_for_delete_servers(project, env)
|
||||||
|
body = {
|
||||||
|
deploy_env: env,
|
||||||
|
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
|
end
|
||||||
|
|||||||
@ -41,6 +41,9 @@ class Stack < Handler
|
|||||||
else
|
else
|
||||||
output
|
output
|
||||||
end
|
end
|
||||||
|
when :change_stack_template
|
||||||
|
change_stack_template_handler
|
||||||
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -136,6 +139,16 @@ class Stack < Handler
|
|||||||
puts response.inspect
|
puts response.inspect
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def provider_stacks(provider)
|
def provider_stacks(provider)
|
||||||
|
|||||||
@ -3,7 +3,7 @@ require "set"
|
|||||||
|
|
||||||
class ProjectOptions < CommonOptions
|
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
|
def initialize args, def_options
|
||||||
super(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_add_params = [id, "USER_NAME", "[USER_NAME ...]"]
|
||||||
self.user_delete_params = [id, "USER_NAME", "[USER_NAME ...]"]
|
self.user_delete_params = [id, "USER_NAME", "[USER_NAME ...]"]
|
||||||
self.test_params = [id, env]
|
self.test_params = [id, env]
|
||||||
|
self.delete_servers_params = [id, "[#{env}]"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_options
|
def create_options
|
||||||
@ -65,7 +66,6 @@ class ProjectOptions < CommonOptions
|
|||||||
# end
|
# end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_add_options
|
def user_add_options
|
||||||
@ -94,4 +94,10 @@ class ProjectOptions < CommonOptions
|
|||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|||||||
@ -2,7 +2,7 @@ require "devops-client/options/common_options"
|
|||||||
|
|
||||||
class StackOptions < CommonOptions
|
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
|
def initialize args, def_options
|
||||||
super(args, def_options)
|
super(args, def_options)
|
||||||
@ -15,6 +15,7 @@ class StackOptions < CommonOptions
|
|||||||
self.resources_params = ["STACK"]
|
self.resources_params = ["STACK"]
|
||||||
self.deploy_params = ["STACK"]
|
self.deploy_params = ["STACK"]
|
||||||
self.reserve_params = ["STACK"]
|
self.reserve_params = ["STACK"]
|
||||||
|
self.change_stack_template_params = %w(STACK STACK_TEMPLATE)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_options
|
def create_options
|
||||||
|
|||||||
@ -27,6 +27,8 @@ module Output
|
|||||||
when :test
|
when :test
|
||||||
title = I18n.t("output.title.project.test", :project => ARGV[2], :env => ARGV[3])
|
title = I18n.t("output.title.project.test", :project => ARGV[2], :env => ARGV[3])
|
||||||
create_test(@data)
|
create_test(@data)
|
||||||
|
when :delete_servers
|
||||||
|
return delete_servers_output
|
||||||
else
|
else
|
||||||
title = I18n.t("output.title.project.list")
|
title = I18n.t("output.title.project.list")
|
||||||
create_list(@data)
|
create_list(@data)
|
||||||
@ -149,5 +151,22 @@ module Output
|
|||||||
headers_and_rows(stacks, fields_to_output)
|
headers_and_rows(stacks, fields_to_output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_servers_output
|
||||||
|
output = ''
|
||||||
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|||||||
@ -343,6 +343,8 @@ en:
|
|||||||
deploy_env: "Delete user from deploy environment"
|
deploy_env: "Delete user from deploy environment"
|
||||||
deploy:
|
deploy:
|
||||||
servers: "Servers list (comma separated) for deploy"
|
servers: "Servers list (comma separated) for deploy"
|
||||||
|
delete_servers:
|
||||||
|
dry_run: "Only show servers to delete, no real deleting"
|
||||||
script:
|
script:
|
||||||
params: Script arguments (comma separated list)
|
params: Script arguments (comma separated list)
|
||||||
server:
|
server:
|
||||||
|
|||||||
3
devops-service/.rspec
Normal file
3
devops-service/.rspec
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
--color
|
||||||
|
--format progress
|
||||||
|
--require spec_helper
|
||||||
@ -23,12 +23,17 @@ gem 'hooks'
|
|||||||
|
|
||||||
#gem "devops-nibr", :path => "../../devops-nibr"
|
#gem "devops-nibr", :path => "../../devops-nibr"
|
||||||
|
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'cucumber'
|
gem 'cucumber'
|
||||||
gem 'test-unit'
|
gem 'test-unit'
|
||||||
gem 'httpclient'
|
gem 'httpclient'
|
||||||
|
gem 'rspec', '~>3.3'
|
||||||
|
gem 'factory_girl', '~>4.5'
|
||||||
|
gem 'activesupport'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :devepoment do
|
group :devepoment do
|
||||||
gem 'byebug'
|
gem 'byebug'
|
||||||
|
gem 'guard-rspec', require: false
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,6 +2,12 @@ GEM
|
|||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (2.3.1)
|
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)
|
addressable (2.3.8)
|
||||||
backports (3.6.4)
|
backports (3.6.4)
|
||||||
bson (1.12.3)
|
bson (1.12.3)
|
||||||
@ -60,6 +66,8 @@ GEM
|
|||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
eventmachine (1.0.7)
|
eventmachine (1.0.7)
|
||||||
excon (0.45.3)
|
excon (0.45.3)
|
||||||
|
factory_girl (4.5.0)
|
||||||
|
activesupport (>= 3.0.0)
|
||||||
ffi (1.9.9)
|
ffi (1.9.9)
|
||||||
ffi-yajl (1.4.0)
|
ffi-yajl (1.4.0)
|
||||||
ffi (~> 1.5)
|
ffi (~> 1.5)
|
||||||
@ -163,18 +171,38 @@ GEM
|
|||||||
formatador (0.2.5)
|
formatador (0.2.5)
|
||||||
gherkin (2.12.2)
|
gherkin (2.12.2)
|
||||||
multi_json (~> 1.3)
|
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)
|
hashie (2.1.2)
|
||||||
highline (1.7.2)
|
highline (1.7.2)
|
||||||
hooks (0.4.0)
|
hooks (0.4.0)
|
||||||
uber (~> 0.0.4)
|
uber (~> 0.0.4)
|
||||||
httpclient (2.6.0.1)
|
httpclient (2.6.0.1)
|
||||||
|
i18n (0.7.0)
|
||||||
inflecto (0.0.2)
|
inflecto (0.0.2)
|
||||||
ipaddress (0.8.0)
|
ipaddress (0.8.0)
|
||||||
json (1.8.3)
|
json (1.8.3)
|
||||||
libyajl2 (1.2.0)
|
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)
|
method_source (0.8.2)
|
||||||
mime-types (1.25.1)
|
mime-types (1.25.1)
|
||||||
mini_portile (0.6.2)
|
mini_portile (0.6.2)
|
||||||
|
minitest (5.8.2)
|
||||||
mixlib-authentication (1.3.0)
|
mixlib-authentication (1.3.0)
|
||||||
mixlib-log
|
mixlib-log
|
||||||
mixlib-cli (1.5.0)
|
mixlib-cli (1.5.0)
|
||||||
@ -185,6 +213,7 @@ GEM
|
|||||||
bson (= 1.12.3)
|
bson (= 1.12.3)
|
||||||
multi_json (1.7.8)
|
multi_json (1.7.8)
|
||||||
multi_test (0.1.2)
|
multi_test (0.1.2)
|
||||||
|
nenv (0.2.0)
|
||||||
net-dhcp (1.3.2)
|
net-dhcp (1.3.2)
|
||||||
net-scp (1.2.1)
|
net-scp (1.2.1)
|
||||||
net-ssh (>= 2.6.5)
|
net-ssh (>= 2.6.5)
|
||||||
@ -196,6 +225,9 @@ GEM
|
|||||||
net-ssh-gateway (>= 1.2.0)
|
net-ssh-gateway (>= 1.2.0)
|
||||||
nokogiri (1.6.6.2)
|
nokogiri (1.6.6.2)
|
||||||
mini_portile (~> 0.6.0)
|
mini_portile (~> 0.6.0)
|
||||||
|
notiffany (0.0.8)
|
||||||
|
nenv (~> 0.1)
|
||||||
|
shellany (~> 0.0)
|
||||||
ohai (8.0.1)
|
ohai (8.0.1)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
ffi-yajl (~> 1.1)
|
ffi-yajl (~> 1.1)
|
||||||
@ -222,6 +254,9 @@ GEM
|
|||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rake (10.2.0)
|
rake (10.2.0)
|
||||||
|
rb-fsevent (0.9.6)
|
||||||
|
rb-inotify (0.9.5)
|
||||||
|
ffi (>= 0.5.0)
|
||||||
redis (3.2.1)
|
redis (3.2.1)
|
||||||
redis-namespace (1.5.2)
|
redis-namespace (1.5.2)
|
||||||
redis (~> 3.0, >= 3.0.4)
|
redis (~> 3.0, >= 3.0.4)
|
||||||
@ -249,6 +284,7 @@ GEM
|
|||||||
rspec (~> 3.0)
|
rspec (~> 3.0)
|
||||||
rspec-its
|
rspec-its
|
||||||
specinfra (~> 2.35)
|
specinfra (~> 2.35)
|
||||||
|
shellany (0.0.1)
|
||||||
sidekiq (3.2.6)
|
sidekiq (3.2.6)
|
||||||
celluloid (= 0.15.2)
|
celluloid (= 0.15.2)
|
||||||
connection_pool (>= 2.0.0)
|
connection_pool (>= 2.0.0)
|
||||||
@ -281,8 +317,12 @@ GEM
|
|||||||
daemons (>= 1.0.9)
|
daemons (>= 1.0.9)
|
||||||
eventmachine (>= 0.12.6)
|
eventmachine (>= 0.12.6)
|
||||||
rack (>= 1.0.0)
|
rack (>= 1.0.0)
|
||||||
|
thor (0.19.1)
|
||||||
|
thread_safe (0.3.5)
|
||||||
tilt (1.4.1)
|
tilt (1.4.1)
|
||||||
timers (1.1.0)
|
timers (1.1.0)
|
||||||
|
tzinfo (1.2.2)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
uber (0.0.13)
|
uber (0.0.13)
|
||||||
uuidtools (2.1.5)
|
uuidtools (2.1.5)
|
||||||
wisper (1.6.0)
|
wisper (1.6.0)
|
||||||
@ -292,11 +332,14 @@ PLATFORMS
|
|||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
activesupport
|
||||||
bson_ext
|
bson_ext
|
||||||
byebug
|
byebug
|
||||||
chef (>= 12)
|
chef (>= 12)
|
||||||
cucumber
|
cucumber
|
||||||
|
factory_girl (~> 4.5)
|
||||||
fog (~> 1.20)
|
fog (~> 1.20)
|
||||||
|
guard-rspec
|
||||||
hooks
|
hooks
|
||||||
httpclient
|
httpclient
|
||||||
mime-types (~> 1.25.1)
|
mime-types (~> 1.25.1)
|
||||||
@ -306,6 +349,7 @@ DEPENDENCIES
|
|||||||
rack (= 1.5.2)
|
rack (= 1.5.2)
|
||||||
rack-accept-media-types
|
rack-accept-media-types
|
||||||
rake (= 10.2.0)
|
rake (= 10.2.0)
|
||||||
|
rspec (~> 3.3)
|
||||||
sidekiq (= 3.2.6)
|
sidekiq (= 3.2.6)
|
||||||
sinatra (= 1.4.5)
|
sinatra (= 1.4.5)
|
||||||
sinatra-contrib
|
sinatra-contrib
|
||||||
|
|||||||
44
devops-service/Guardfile
Normal file
44
devops-service/Guardfile
Normal file
@ -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
|
||||||
@ -85,7 +85,7 @@ module Devops
|
|||||||
begin
|
begin
|
||||||
deploy_info = create_deploy_info(s, project, body["build_number"])
|
deploy_info = create_deploy_info(s, project, body["build_number"])
|
||||||
deploy_info["run_list"] = run_list if run_list
|
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)
|
status.push(res)
|
||||||
rescue DeployInfoError => e
|
rescue DeployInfoError => e
|
||||||
msg = "Can not get deploy info: " + e.message
|
msg = "Can not get deploy info: " + e.message
|
||||||
|
|||||||
@ -3,6 +3,8 @@ require "db/mongo/models/project"
|
|||||||
require "workers/project_test_worker"
|
require "workers/project_test_worker"
|
||||||
require "app/api2/parsers/project"
|
require "app/api2/parsers/project"
|
||||||
require "lib/project/type/types_factory"
|
require "lib/project/type/types_factory"
|
||||||
|
require "lib/executors/server_executor"
|
||||||
|
|
||||||
require_relative "../helpers/version_2.rb"
|
require_relative "../helpers/version_2.rb"
|
||||||
require_relative "request_handler"
|
require_relative "request_handler"
|
||||||
|
|
||||||
@ -223,7 +225,7 @@ module Devops
|
|||||||
# мы не можем указать один build_number для всех окружений, поэтому nil
|
# мы не можем указать один build_number для всех окружений, поэтому nil
|
||||||
deploy_info_buf[s.deploy_env] = project.deploy_info(deploy_env_model, nil)
|
deploy_info_buf[s.deploy_env] = project.deploy_info(deploy_env_model, nil)
|
||||||
end
|
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
|
end
|
||||||
status
|
status
|
||||||
end
|
end
|
||||||
@ -298,6 +300,32 @@ module Devops
|
|||||||
return [jid]
|
return [jid]
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -120,7 +120,7 @@ module Devops
|
|||||||
provider = s.provider_instance
|
provider = s.provider_instance
|
||||||
r = provider.pause_server s
|
r = provider.pause_server s
|
||||||
if r.nil?
|
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"
|
"Server with instance ID '#{s.id}' and node name '#{node_name}' is paused"
|
||||||
else
|
else
|
||||||
@ -135,7 +135,7 @@ module Devops
|
|||||||
provider = s.provider_instance
|
provider = s.provider_instance
|
||||||
r = provider.unpause_server s
|
r = provider.unpause_server s
|
||||||
if r.nil?
|
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"
|
"Server with instance ID '#{s.id}' and node name '#{node_name}' is unpaused"
|
||||||
else
|
else
|
||||||
@ -149,7 +149,7 @@ module Devops
|
|||||||
Devops::Db.connector.check_project_auth s.project, s.deploy_env, user
|
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?
|
raise ConflictException.new("Server '#{node_name}' already reserved") unless s.reserved_by.nil?
|
||||||
s.reserved_by = user
|
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
|
end
|
||||||
|
|
||||||
def unreserve_server node_name
|
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
|
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?
|
raise ConflictException.new("Server '#{node_name}' is not reserved") if s.reserved_by.nil?
|
||||||
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
|
end
|
||||||
|
|
||||||
# TODO: check bootstrap template name
|
# TODO: check bootstrap template name
|
||||||
@ -169,7 +169,7 @@ module Devops
|
|||||||
DevopsLogger.logger.debug "Bootstrap certificate path: #{cert.path}"
|
DevopsLogger.logger.debug "Bootstrap certificate path: #{cert.path}"
|
||||||
#bootstrap s, out, cert.path, logger
|
#bootstrap s, out, cert.path, logger
|
||||||
options[:cert_path] = cert.path
|
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)
|
r = executor.two_phase_bootstrap(options)
|
||||||
str = nil
|
str = nil
|
||||||
r = if check_server(s)
|
r = if check_server(s)
|
||||||
@ -322,8 +322,8 @@ module Devops
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_last_operation_type_and_save(server, operation_type)
|
def set_last_operation_and_save(server, operation_type)
|
||||||
server.set_last_operation(operation_type)
|
server.set_last_operation(operation_type, parser.current_user)
|
||||||
Devops::Db.connector.server_update(server)
|
Devops::Db.connector.server_update(server)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -155,7 +155,7 @@ module Devops
|
|||||||
deploy_info = project.deploy_info(stack.deploy_env, nil)
|
deploy_info = project.deploy_info(stack.deploy_env, nil)
|
||||||
servers.each do |s|
|
servers.each do |s|
|
||||||
begin
|
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)
|
status.push(res)
|
||||||
rescue DeployInfoError => e
|
rescue DeployInfoError => e
|
||||||
msg = "Can not get deploy info: " + e.message
|
msg = "Can not get deploy info: " + e.message
|
||||||
@ -203,6 +203,13 @@ module Devops
|
|||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -28,6 +28,14 @@ module Devops
|
|||||||
check_param val, String, msg, _nil, empty
|
check_param val, String, msg, _nil, empty
|
||||||
end
|
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
|
def check_array val, msg, vals_type=String, _nil=false, empty=false
|
||||||
check_param val, Array, msg, _nil, empty
|
check_param val, Array, msg, _nil, empty
|
||||||
val.each {|v| raise Devops::ValidationError.new(msg) unless v.is_a?(vals_type)} unless val.nil?
|
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
|
||||||
end
|
end
|
||||||
if val.is_a?(type)
|
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
|
val
|
||||||
else
|
else
|
||||||
raise Devops::ValidationError.new(msg)
|
raise Devops::ValidationError.new(msg)
|
||||||
|
|||||||
@ -97,6 +97,13 @@ module Devops
|
|||||||
servers = check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true)
|
servers = check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true)
|
||||||
return deploy_env, servers
|
return deploy_env, servers
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -38,6 +38,12 @@ module Devops
|
|||||||
@body
|
@body
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -166,10 +166,37 @@ module Devops
|
|||||||
# "id": "nstance id"
|
# "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")
|
check_privileges("project", "r")
|
||||||
json Devops::API2_0::Handler::Project.new(request).project_servers(project).map(&:to_hash)
|
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
|
# Get project stacks
|
||||||
#
|
#
|
||||||
|
|||||||
@ -187,6 +187,25 @@ module Devops
|
|||||||
}
|
}
|
||||||
app.multi_routes "/stack/:id/tags", {:headers => [:content_type]}, hash
|
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"
|
puts "Stack routes initialized"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
module StackCommands
|
module StackCommands
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
|
RESULT_CODES = {
|
||||||
|
stack_rolled_back: 1,
|
||||||
|
unkown_status: 2,
|
||||||
|
timeout: 3,
|
||||||
|
error: 5
|
||||||
|
}
|
||||||
|
|
||||||
def self.result_codes
|
def self.result_codes
|
||||||
{
|
RESULT_CODES
|
||||||
stack_rolled_back: 1,
|
|
||||||
unkown_status: 2,
|
|
||||||
timeout: 3,
|
|
||||||
error: 5
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.result_code(code)
|
def self.result_code(code)
|
||||||
@ -21,12 +23,18 @@ module StackCommands
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
out << "Syncing stack '#{stack.id}'...\n"
|
out << "Syncing stack '#{stack.id}'...\n"
|
||||||
|
events_keys = []
|
||||||
sleep_times.each do |sleep_time|
|
sleep_times.each do |sleep_time|
|
||||||
sleep sleep_time
|
sleep sleep_time
|
||||||
stack.sync_details!
|
stack.sync_details!
|
||||||
|
stack.events.each do |event|
|
||||||
|
unless events_keys.include?(event["event_id"])
|
||||||
|
events_keys << event["event_id"]
|
||||||
|
out.puts "#{event["timestamp"]} - #{event["status"]}: #{event["reason"]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
case stack.stack_status
|
case stack.stack_status
|
||||||
when 'CREATE_IN_PROGRESS'
|
when 'CREATE_IN_PROGRESS'
|
||||||
out << "."
|
|
||||||
out.flush
|
out.flush
|
||||||
when 'CREATE_COMPLETE'
|
when 'CREATE_COMPLETE'
|
||||||
mongo.stack_update(stack)
|
mongo.stack_update(stack)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module Devops
|
|||||||
|
|
||||||
def self.inherited(base)
|
def self.inherited(base)
|
||||||
@@applications << base.new
|
@@applications << base.new
|
||||||
puts "Devops application '#{base}' has been added"
|
puts "Devops application '#{base}' has been added" unless ENV['RACK_ENV'] == 'test'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.applications
|
def self.applications
|
||||||
|
|||||||
@ -59,10 +59,7 @@ module Connectors
|
|||||||
set = {"status" => status, "updated_at" => Time.new}
|
set = {"status" => status, "updated_at" => Time.new}
|
||||||
set["job_result_code"] = job_result_code unless job_result_code.nil?
|
set["job_result_code"] = job_result_code unless job_result_code.nil?
|
||||||
collection.update({"_id" => jid}, {"$set" => set})
|
collection.update({"_id" => jid}, {"$set" => set})
|
||||||
end
|
DevopsLogger.logger.info("Report '#{jid}' status has been changed to '#{status}'")
|
||||||
|
|
||||||
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}})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_report_server_data id, chef_node_name, host
|
def set_report_server_data id, chef_node_name, host
|
||||||
|
|||||||
@ -12,21 +12,7 @@ module Devops
|
|||||||
class CloudDeployEnv < DeployEnvBase
|
class CloudDeployEnv < DeployEnvBase
|
||||||
|
|
||||||
attr_accessor :flavor, :image, :subnets, :groups, :stack_template
|
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,
|
set_validators ::Validators::DeployEnv::Flavor,
|
||||||
::Validators::DeployEnv::Image,
|
::Validators::DeployEnv::Image,
|
||||||
::Validators::DeployEnv::Groups,
|
::Validators::DeployEnv::Groups,
|
||||||
@ -46,8 +32,7 @@ module Devops
|
|||||||
# ::Validators::FieldValidator::Groups.new
|
# ::Validators::FieldValidator::Groups.new
|
||||||
set_field_validators :stack_template, ::Validators::FieldValidator::Nil,
|
set_field_validators :stack_template, ::Validators::FieldValidator::Nil,
|
||||||
::Validators::FieldValidator::FieldType::String,
|
::Validators::FieldValidator::FieldType::String,
|
||||||
::Validators::FieldValidator::NotEmpty,
|
::Validators::FieldValidator::NotEmpty
|
||||||
# ::Validators::FieldValidator::StackTemplate.new
|
|
||||||
|
|
||||||
def initialize d={}
|
def initialize d={}
|
||||||
super(d)
|
super(d)
|
||||||
|
|||||||
@ -69,7 +69,7 @@ module Devops
|
|||||||
role = res[:new] || res[:exist]
|
role = res[:new] || res[:exist]
|
||||||
self.run_list << "role[#{role[0]}]" unless role.nil?
|
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})
|
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)
|
DevopsLogger.logger.info(msg)
|
||||||
msg
|
msg
|
||||||
end
|
end
|
||||||
|
|||||||
@ -40,13 +40,14 @@ module Devops
|
|||||||
DeployEnvEc2.new(hash)
|
DeployEnvEc2.new(hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def subnets_filter
|
def subnets_filter
|
||||||
networks = provider_instance.networks
|
networks = provider_instance.networks
|
||||||
|
|
||||||
unless self.subnets.empty?
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -4,16 +4,16 @@ module Devops
|
|||||||
module Model
|
module Model
|
||||||
class DeployEnvOpenstack < CloudDeployEnv
|
class DeployEnvOpenstack < CloudDeployEnv
|
||||||
|
|
||||||
types :identifier => {:type => String, :empty => false},
|
# types :identifier => {:type => String, :empty => false},
|
||||||
:image => {:type => String, :empty => false},
|
# :image => {:type => String, :empty => false},
|
||||||
:flavor => {:type => String, :empty => false},
|
# :flavor => {:type => String, :empty => false},
|
||||||
:provider => {:type => String, :empty => false},
|
# :provider => {:type => String, :empty => false},
|
||||||
:expires => {:type => String, :empty => false, :nil => true},
|
# :expires => {:type => String, :empty => false, :nil => true},
|
||||||
:run_list => {:type => Array, :empty => true},
|
# :run_list => {:type => Array, :empty => true},
|
||||||
:users => {:type => Array, :empty => true},
|
# :users => {:type => Array, :empty => true},
|
||||||
:subnets => {:type => Array, :empty => true},
|
# :subnets => {:type => Array, :empty => true},
|
||||||
:groups => {:type => Array, :empty => false},
|
# :groups => {:type => Array, :empty => false},
|
||||||
:stack_template => {:type => String, :empty => false, :nil => true}
|
# :stack_template => {:type => String, :empty => false, :nil => true}
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
set_validators ::Validators::DeployEnv::RunList,
|
set_validators ::Validators::DeployEnv::RunList,
|
||||||
@ -31,8 +31,6 @@ module Devops
|
|||||||
DeployEnvOpenstack.new(hash)
|
DeployEnvOpenstack.new(hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def subnets_filter
|
def subnets_filter
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|||||||
@ -7,6 +7,7 @@ module Devops
|
|||||||
module Model
|
module Model
|
||||||
class DeployEnvStatic < DeployEnvBase
|
class DeployEnvStatic < DeployEnvBase
|
||||||
|
|
||||||
|
# TODO: all code except self.create could be safely removed
|
||||||
types :identifier => {:type => String, :empty => false},
|
types :identifier => {:type => String, :empty => false},
|
||||||
:provider => {:type => String, :empty => false},
|
:provider => {:type => String, :empty => false},
|
||||||
:expires => {:type => String, :empty => false, :nil => true},
|
:expires => {:type => String, :empty => false, :nil => true},
|
||||||
|
|||||||
@ -65,13 +65,12 @@ module Devops
|
|||||||
end
|
end
|
||||||
|
|
||||||
def to_hash_without_id
|
def to_hash_without_id
|
||||||
o = {
|
{
|
||||||
"provider" => self.provider,
|
"provider" => provider,
|
||||||
"name" => self.name,
|
"name" => name,
|
||||||
"remote_user" => self.remote_user
|
"remote_user" => remote_user,
|
||||||
|
"bootstrap_template" => bootstrap_template
|
||||||
}
|
}
|
||||||
o["bootstrap_template"] = self.bootstrap_template
|
|
||||||
o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create_from_json! json
|
def self.create_from_json! json
|
||||||
|
|||||||
@ -39,11 +39,10 @@ module Devops
|
|||||||
end
|
end
|
||||||
|
|
||||||
def to_hash_without_id
|
def to_hash_without_id
|
||||||
o = {
|
{
|
||||||
"path" => self.path,
|
"path" => self.path,
|
||||||
"scope" => self.scope
|
"scope" => self.scope
|
||||||
}
|
}
|
||||||
o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -59,6 +59,7 @@ module Devops
|
|||||||
# TODO: we should validate type in request parser
|
# TODO: we should validate type in request parser
|
||||||
# self.validate_fields_types
|
# self.validate_fields_types
|
||||||
self.class.validate_model(self)
|
self.class.validate_model(self)
|
||||||
|
validate_fields!
|
||||||
true
|
true
|
||||||
rescue InvalidRecord => e
|
rescue InvalidRecord => e
|
||||||
error_message = self.build_error_message(e.message)
|
error_message = self.build_error_message(e.message)
|
||||||
@ -81,6 +82,13 @@ module Devops
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
validate!
|
||||||
|
true
|
||||||
|
rescue InvalidRecord
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def build_error_message(message)
|
def build_error_message(message)
|
||||||
# overrided in descendants
|
# overrided in descendants
|
||||||
message
|
message
|
||||||
@ -157,6 +165,7 @@ module Devops
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# validate field value
|
# validate field value
|
||||||
# if method validate! returns false, then stop validation without error
|
# if method validate! returns false, then stop validation without error
|
||||||
def set_field_validators field, *validators
|
def set_field_validators field, *validators
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
require "db/mongo/models/mongo_model"
|
require "db/mongo/models/mongo_model"
|
||||||
require "db/mongo/models/model_with_provider"
|
require "db/mongo/models/model_with_provider"
|
||||||
require "db/validators/deploy_env/run_list"
|
require "db/validators/deploy_env/run_list"
|
||||||
|
require "providers/static"
|
||||||
|
|
||||||
module Devops
|
module Devops
|
||||||
module Model
|
module Model
|
||||||
@ -10,16 +10,22 @@ module Devops
|
|||||||
include ModelWithProvider
|
include ModelWithProvider
|
||||||
|
|
||||||
module OperationType
|
module OperationType
|
||||||
CREATION = :creation
|
# we store strings in mongo, so it's better not to use symbols
|
||||||
DEPLOY = :deploy
|
CREATION = 'creation'
|
||||||
RESERVE = :reserve
|
DEPLOY = 'deploy'
|
||||||
UNRESERVE = :unreserve
|
RESERVE = 'reserve'
|
||||||
PAUSE = :pause
|
UNRESERVE = 'unreserve'
|
||||||
UNPAUSE = :unpause
|
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
|
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 :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},
|
types :id => {:type => String, :empty => false},
|
||||||
:provider => {:type => String, :empty => false},
|
:provider => {:type => String, :empty => false},
|
||||||
@ -39,13 +45,13 @@ module Devops
|
|||||||
|
|
||||||
def self.fields
|
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",
|
["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
|
end
|
||||||
|
|
||||||
def initialize s={}
|
def initialize s={}
|
||||||
self.set_provider(s)
|
self.set_provider(s)
|
||||||
self.chef_node_name = s["chef_node_name"]
|
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.remote_user = s["remote_user"]
|
||||||
self.project = s["project"]
|
self.project = s["project"]
|
||||||
self.deploy_env = s["deploy_env"]
|
self.deploy_env = s["deploy_env"]
|
||||||
@ -57,21 +63,13 @@ module Devops
|
|||||||
self.reserved_by = s["reserved_by"]
|
self.reserved_by = s["reserved_by"]
|
||||||
self.stack = s["stack"]
|
self.stack = s["stack"]
|
||||||
self.run_list = s["run_list"] || []
|
self.run_list = s["run_list"] || []
|
||||||
self.last_operation_at = s["last_operation_at"]
|
self.last_operation = s["last_operation"]
|
||||||
self.last_operation_type = s["last_operation_type"]
|
|
||||||
self
|
self
|
||||||
end
|
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!
|
def validate!
|
||||||
|
# remove it after updating from .types to validators
|
||||||
|
validate_fields_types
|
||||||
super
|
super
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
@ -90,8 +88,7 @@ module Devops
|
|||||||
"reserved_by" => self.reserved_by,
|
"reserved_by" => self.reserved_by,
|
||||||
"stack" => stack,
|
"stack" => stack,
|
||||||
"run_list" => self.run_list,
|
"run_list" => self.run_list,
|
||||||
"last_operation_at" => self.last_operation_at,
|
"last_operation" => self.last_operation
|
||||||
"last_operation_type" => self.last_operation_type
|
|
||||||
}.merge(provider_hash).delete_if { |k,v| v.nil? }
|
}.merge(provider_hash).delete_if { |k,v| v.nil? }
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -114,9 +111,13 @@ module Devops
|
|||||||
self.provider == Provider::Static::PROVIDER
|
self.provider == Provider::Static::PROVIDER
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_last_operation(operation_type)
|
def set_last_operation(operation_type, user)
|
||||||
self.last_operation_type = operation_type
|
raise ArgumentError unless OperationType.supported_type?(operation_type)
|
||||||
self.last_operation_at = Time.now
|
self.last_operation = {
|
||||||
|
'type' => operation_type,
|
||||||
|
'date' => Time.now,
|
||||||
|
'user' => user
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -7,7 +7,7 @@ module Devops
|
|||||||
|
|
||||||
include ModelWithProvider
|
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},
|
types id: {type: String, empty: false},
|
||||||
provider: {type: String, empty: false},
|
provider: {type: String, empty: false},
|
||||||
@ -19,6 +19,38 @@ module Devops
|
|||||||
run_list: {type: Array, value_type: String, empty: true, nil: true}
|
run_list: {type: Array, value_type: String, empty: true, nil: true}
|
||||||
# details: {type: Hash, nil: true} # Hash type isn't supported yet
|
# 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={}
|
def initialize attrs={}
|
||||||
# self.provider = self.class.provider
|
# self.provider = self.class.provider
|
||||||
self.set_provider(attrs)
|
self.set_provider(attrs)
|
||||||
@ -34,6 +66,7 @@ module Devops
|
|||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: use string keys
|
||||||
def to_hash_without_id
|
def to_hash_without_id
|
||||||
{
|
{
|
||||||
project: project,
|
project: project,
|
||||||
@ -44,7 +77,8 @@ module Devops
|
|||||||
# details are required to proper status handling
|
# details are required to proper status handling
|
||||||
details: bson_safe_details,
|
details: bson_safe_details,
|
||||||
stack_status: stack_status,
|
stack_status: stack_status,
|
||||||
owner: owner
|
owner: owner,
|
||||||
|
run_list: run_list
|
||||||
}.merge(provider_hash)
|
}.merge(provider_hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -67,6 +101,7 @@ module Devops
|
|||||||
|
|
||||||
def sync_details!
|
def sync_details!
|
||||||
self.details = provider_instance.stack_details(self)
|
self.details = provider_instance.stack_details(self)
|
||||||
|
self.events = provider_instance.stack_events(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def resources
|
def resources
|
||||||
|
|||||||
@ -1,16 +1,19 @@
|
|||||||
|
require 'db/mongo/models/stack/stack_base'
|
||||||
|
|
||||||
module Devops
|
module Devops
|
||||||
module Model
|
module Model
|
||||||
class StackEc2 < StackBase
|
class StackEc2 < StackBase
|
||||||
|
|
||||||
def initialize attr={}
|
def initialize attr={}
|
||||||
self.provider = 'ec2'
|
|
||||||
super(attr)
|
super(attr)
|
||||||
|
self.provider = 'ec2'
|
||||||
end
|
end
|
||||||
|
|
||||||
def stack_status
|
def stack_status
|
||||||
self.details['StackStatus'] if self.details
|
self.details['StackStatus'] if self.details
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# creation date is instance of unsupported by mongo class, so stringify it
|
||||||
def bson_safe_details
|
def bson_safe_details
|
||||||
return unless details
|
return unless details
|
||||||
result = details.dup
|
result = details.dup
|
||||||
@ -18,6 +21,23 @@ module Devops
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_in_cloud!(params)
|
||||||
|
parameters = params.keep_if do |key|
|
||||||
|
%w(Parameters TemplateBody TemplateURL Capabilities).include?(key)
|
||||||
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
require 'db/mongo/models/stack/stack_base'
|
||||||
module Devops
|
module Devops
|
||||||
module Model
|
module Model
|
||||||
class StackOpenstack < StackBase
|
class StackOpenstack < StackBase
|
||||||
|
|||||||
@ -16,6 +16,23 @@ module Devops
|
|||||||
template_body: {type: String, empty: false},
|
template_body: {type: String, empty: false},
|
||||||
owner: {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
|
set_validators ::Validators::StackTemplate::TemplateContent
|
||||||
|
|
||||||
def initialize(attrs)
|
def initialize(attrs)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ module Devops
|
|||||||
# In Amazon Cloudformation the template file must be stored on an Amazon S3 bucket.
|
# In Amazon Cloudformation the template file must be stored on an Amazon S3 bucket.
|
||||||
attr_accessor :template_url
|
attr_accessor :template_url
|
||||||
|
|
||||||
types template_url: {type: String, empty: false}
|
# types template_url: {type: String, empty: false}
|
||||||
|
|
||||||
def initialize(attrs)
|
def initialize(attrs)
|
||||||
self.template_url = attrs['template_url']
|
self.template_url = attrs['template_url']
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
require "exceptions/invalid_record"
|
require "exceptions/invalid_record"
|
||||||
require "exceptions/invalid_command"
|
require "exceptions/invalid_command"
|
||||||
|
require "exceptions/invalid_privileges"
|
||||||
require "db/mongo/models/mongo_model"
|
require "db/mongo/models/mongo_model"
|
||||||
|
|
||||||
module Devops
|
module Devops
|
||||||
@ -12,10 +13,9 @@ module Devops
|
|||||||
PRIVILEGES = ["r", "w", "x"]
|
PRIVILEGES = ["r", "w", "x"]
|
||||||
PRIVILEGES_REGEX = /^r?w?x?$/
|
PRIVILEGES_REGEX = /^r?w?x?$/
|
||||||
|
|
||||||
|
KNOWN_ENTITIES = %w(flavor group image project server key user filter network provider script templates stack_template stack)
|
||||||
|
|
||||||
attr_accessor :id, :password, :privileges, :email
|
attr_accessor :id, :password, :privileges, :email
|
||||||
types :id => {:type => String, :empty => false},
|
|
||||||
:email => {:type => String, :empty => false},
|
|
||||||
:password => {:type => String, :empty => true}
|
|
||||||
|
|
||||||
set_field_validators :id, ::Validators::FieldValidator::NotNil,
|
set_field_validators :id, ::Validators::FieldValidator::NotNil,
|
||||||
::Validators::FieldValidator::FieldType::String,
|
::Validators::FieldValidator::FieldType::String,
|
||||||
@ -54,7 +54,7 @@ module Devops
|
|||||||
if !priv.empty? and PRIVILEGES_REGEX.match(priv).to_s.empty?
|
if !priv.empty? and PRIVILEGES_REGEX.match(priv).to_s.empty?
|
||||||
raise InvalidCommand.new "Invalid privileges '#{priv}'. Available values are '#{PRIVILEGES.join("', '")}'"
|
raise InvalidCommand.new "Invalid privileges '#{priv}'. Available values are '#{PRIVILEGES.join("', '")}'"
|
||||||
end
|
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
|
case cmd
|
||||||
when "all"
|
when "all"
|
||||||
@ -114,22 +114,7 @@ module Devops
|
|||||||
|
|
||||||
def privileges_with_value value, options={}
|
def privileges_with_value value, options={}
|
||||||
privileges = {}
|
privileges = {}
|
||||||
[
|
KNOWN_ENTITIES.each { |t| privileges.store(t, value) }
|
||||||
'flavor',
|
|
||||||
'group',
|
|
||||||
'image',
|
|
||||||
'project',
|
|
||||||
'server',
|
|
||||||
'key',
|
|
||||||
'user',
|
|
||||||
'filter',
|
|
||||||
'network',
|
|
||||||
'provider',
|
|
||||||
'script',
|
|
||||||
'templates',
|
|
||||||
'stack_template',
|
|
||||||
'stack'
|
|
||||||
].each { |t| privileges.store(t, value) }
|
|
||||||
|
|
||||||
privileges.merge(options)
|
privileges.merge(options)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -8,6 +8,8 @@ module Validators
|
|||||||
|
|
||||||
def validate!
|
def validate!
|
||||||
raise InvalidRecord.new(message) unless valid?
|
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
|
end
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
|
|||||||
@ -2,7 +2,8 @@ module Validators
|
|||||||
class DeployEnv::Flavor < Base
|
class DeployEnv::Flavor < Base
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
@model.provider_instance.flavors.detect do |flavor|
|
return true unless @model.flavor
|
||||||
|
available_flavors.detect do |flavor|
|
||||||
flavor['id'] == @model.flavor
|
flavor['id'] == @model.flavor
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -10,5 +11,11 @@ module Validators
|
|||||||
def message
|
def message
|
||||||
"Invalid flavor '#{@model.flavor}'."
|
"Invalid flavor '#{@model.flavor}'."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_flavors
|
||||||
|
@model.provider_instance.flavors
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -2,13 +2,20 @@ module Validators
|
|||||||
class DeployEnv::Groups < Base
|
class DeployEnv::Groups < Base
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
subnets_filter = @model.send(:subnets_filter)
|
return true if @model.groups.nil?
|
||||||
@invalid_groups = @model.groups - @model.provider_instance.groups(subnets_filter).keys
|
@invalid_groups = @model.groups - available_groups
|
||||||
@invalid_groups.empty?
|
@invalid_groups.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
def message
|
||||||
"Invalid groups '#{@invalid_groups.join("', '")}'."
|
"Invalid groups '#{@invalid_groups.join("', '")}'."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_groups
|
||||||
|
subnets_filter = @model.subnets_filter
|
||||||
|
@model.provider_instance.groups(subnets_filter).keys
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -5,8 +5,8 @@ module Validators
|
|||||||
include ::ImageCommands
|
include ::ImageCommands
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
images = get_available_provider_images(::Devops::Db.connector, @model.provider)
|
return true unless @model.image
|
||||||
images.detect do |image|
|
available_images.detect do |image|
|
||||||
image["id"] == @model.image
|
image["id"] == @model.image
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -14,5 +14,11 @@ module Validators
|
|||||||
def message
|
def message
|
||||||
"Invalid image '#{@model.image}'."
|
"Invalid image '#{@model.image}'."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_images
|
||||||
|
get_available_provider_images(::Devops::Db.connector, @model.provider)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,20 +2,23 @@ module Validators
|
|||||||
class DeployEnv::StackTemplate < Base
|
class DeployEnv::StackTemplate < Base
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
if @model.stack_template
|
return true unless @model.stack_template
|
||||||
begin
|
|
||||||
Devops::Db.connector.stack_template(@model.stack_template)
|
available_stack_templates.detect do |template|
|
||||||
true
|
template['id'] == @model.stack_template
|
||||||
rescue RecordNotFound => e
|
|
||||||
false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
def message
|
||||||
"Invalid stack template '#{@model.stack_template}'."
|
"Invalid stack template '#{@model.stack_template}'."
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|||||||
@ -4,7 +4,7 @@ module Validators
|
|||||||
class Flavor < Base
|
class Flavor < Base
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
@model.provider_instance.flavors.detect do |flavor|
|
available_flavors.detect do |flavor|
|
||||||
flavor['id'] == @value
|
flavor['id'] == @value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -12,6 +12,12 @@ module Validators
|
|||||||
def message
|
def message
|
||||||
"Invalid flavor '#{@value}'."
|
"Invalid flavor '#{@value}'."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_flavors
|
||||||
|
@model.provider_instance.flavors
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -7,8 +7,7 @@ module Validators
|
|||||||
include ::ImageCommands
|
include ::ImageCommands
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
images = get_available_provider_images(::Devops::Db.connector, @model.provider)
|
available_images.detect do |image|
|
||||||
images.detect do |image|
|
|
||||||
image["id"] == @value
|
image["id"] == @value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -16,6 +15,12 @@ module Validators
|
|||||||
def message
|
def message
|
||||||
"Invalid image '#{@value}'."
|
"Invalid image '#{@value}'."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_images
|
||||||
|
get_available_provider_images(::Devops::Db.connector, @model.provider)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,13 +2,18 @@ module Validators
|
|||||||
class Helpers::Users < Base
|
class Helpers::Users < Base
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
mongo_users = ::Devops::Db.connector.users_names(@model)
|
@nonexistent_users = (@model || []) - available_users
|
||||||
@nonexistent_users = @model - mongo_users
|
|
||||||
@nonexistent_users.empty?
|
@nonexistent_users.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
def message
|
||||||
Devops::Messages.t("project.deploy_env.validation.users.not_exist", users: @nonexistent_users.join("', '"))
|
Devops::Messages.t("project.deploy_env.validation.users.not_exist", users: @nonexistent_users.join("', '"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_users
|
||||||
|
::Devops::Db.connector.users_names(@model)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -9,8 +9,7 @@ module Validators
|
|||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
if @model.bootstrap_template
|
if @model.bootstrap_template
|
||||||
templates = get_templates
|
available_templates.include?(@model.bootstrap_template)
|
||||||
templates.include?(@model.bootstrap_template)
|
|
||||||
else
|
else
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
@ -19,6 +18,12 @@ module Validators
|
|||||||
def message
|
def message
|
||||||
"Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'"
|
"Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_templates
|
||||||
|
get_templates
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -5,12 +5,17 @@ module Validators
|
|||||||
include ImageCommands
|
include ImageCommands
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
images = get_available_provider_images(::Devops::Db.connector, @model.provider)
|
available_images.map{|i| i["id"]}.include?(@model.id)
|
||||||
images.map{|i| i["id"]}.include?(@model.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
def message
|
||||||
"Invalid image id '#{@model.id}' for provider '#{@model.provider}', please check image filters"
|
"Invalid image id '#{@model.id}' for provider '#{@model.provider}', please check image filters"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def available_images
|
||||||
|
get_available_provider_images(::Devops::Db.connector, @model.provider)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -35,7 +35,7 @@ module Devops
|
|||||||
|
|
||||||
before_deploy :create_run_list
|
before_deploy :create_run_list
|
||||||
|
|
||||||
def initialize server, out
|
def initialize server, out, options={}
|
||||||
if server
|
if server
|
||||||
@project = Devops::Db.connector.project(server.project)
|
@project = Devops::Db.connector.project(server.project)
|
||||||
@deploy_env = @project.deploy_env(server.deploy_env)
|
@deploy_env = @project.deploy_env(server.deploy_env)
|
||||||
@ -44,6 +44,7 @@ module Devops
|
|||||||
@server = server
|
@server = server
|
||||||
@out = out
|
@out = out
|
||||||
@out.class.send(:define_method, :flush) { } unless @out.respond_to?(:flush)
|
@out.class.send(:define_method, :flush) { } unless @out.respond_to?(:flush)
|
||||||
|
@current_user = options[:current_user]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.result_code(symbolic_code)
|
def self.result_code(symbolic_code)
|
||||||
@ -74,9 +75,19 @@ module Devops
|
|||||||
@server
|
@server
|
||||||
end
|
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
|
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)
|
@server.set_last_operation(Devops::Model::Server::OperationType::CREATION, options["created_by"])
|
||||||
provider = @deploy_env.provider_instance
|
provider = @deploy_env.provider_instance
|
||||||
mongo = ::Devops::Db.connector
|
mongo = ::Devops::Db.connector
|
||||||
begin
|
begin
|
||||||
@ -116,7 +127,9 @@ module Devops
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
rescue => e
|
rescue => e
|
||||||
@out << e.message
|
@out.puts e.message
|
||||||
|
@out.puts e.backtrace.join("\n")
|
||||||
|
|
||||||
DevopsLogger.logger.error e.message
|
DevopsLogger.logger.error e.message
|
||||||
roll_back
|
roll_back
|
||||||
mongo.server_delete @server.id
|
mongo.server_delete @server.id
|
||||||
@ -215,6 +228,7 @@ module Devops
|
|||||||
bootstrap_options.push "-N #{@server.chef_node_name}" if @server.chef_node_name
|
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 "--sudo" unless @server.remote_user == "root"
|
||||||
bootstrap_options.push "-t #{options[:bootstrap_template]}" if options[:bootstrap_template]
|
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]
|
rl = options[:run_list]
|
||||||
bootstrap_options.push "-r #{rl.join(",")}" unless rl.nil? or rl.empty?
|
bootstrap_options.push "-r #{rl.join(",")}" unless rl.nil? or rl.empty?
|
||||||
bootstrap_options.push "-c #{options[:config]}" if options[:config]
|
bootstrap_options.push "-c #{options[:config]}" if options[:config]
|
||||||
@ -360,7 +374,7 @@ module Devops
|
|||||||
deploy_info.delete("use_json_file")
|
deploy_info.delete("use_json_file")
|
||||||
json = nil
|
json = nil
|
||||||
dir = DevopsConfig.config[:project_info_dir]
|
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)
|
path = File.join(dir, file)
|
||||||
if File.exists?(path)
|
if File.exists?(path)
|
||||||
json = File.read(path)
|
json = File.read(path)
|
||||||
@ -387,17 +401,19 @@ module Devops
|
|||||||
k = Devops::Db.connector.key(@server.key)
|
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
|
r = /Chef\sClient\sfinished/i
|
||||||
if lline[r].nil?
|
|
||||||
1
|
if lline && lline[r]
|
||||||
else
|
|
||||||
@out << "\nAfter deploy hooks...\n"
|
@out << "\nAfter deploy hooks...\n"
|
||||||
res = self.run_hook(:after_deploy, @out, deploy_info)
|
res = self.run_hook(:after_deploy, @out, deploy_info)
|
||||||
@out << "Done\n"
|
@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)
|
Devops::Db.connector.server_update(@server)
|
||||||
|
|
||||||
0
|
0
|
||||||
|
else
|
||||||
|
@out << "An error occured during knife command executing"
|
||||||
|
1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -43,5 +43,9 @@ module Provider
|
|||||||
def set_stack_tags stack, out=""
|
def set_stack_tags stack, out=""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stack_events(stack)
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -110,7 +110,8 @@ module Provider
|
|||||||
vpcId = nil
|
vpcId = nil
|
||||||
unless subnets.empty?
|
unless subnets.empty?
|
||||||
options["SubnetId"] = subnets[0]
|
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?
|
if vpcId.nil?
|
||||||
out << "Can not get 'vpcId' by subnet name '#{options["SubnetId"]}'\n"
|
out << "Can not get 'vpcId' by subnet name '#{options["SubnetId"]}'\n"
|
||||||
return false
|
return false
|
||||||
@ -152,7 +153,8 @@ module Provider
|
|||||||
end
|
end
|
||||||
s.public_ip = details["ipAddress"]
|
s.public_ip = details["ipAddress"]
|
||||||
s.private_ip = details["privateIpAddress"]
|
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 << "\nServer tags: #{tags.inspect}\n"
|
||||||
out << "\nDone\n\n"
|
out << "\nDone\n\n"
|
||||||
out << s.info
|
out << s.info
|
||||||
@ -160,16 +162,14 @@ module Provider
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_server_tags s
|
def server_tags server
|
||||||
tags = {
|
{
|
||||||
"Name" => s.chef_node_name,
|
"Name" => server.chef_node_name,
|
||||||
"cid:project" => s.project,
|
"cid:project" => server.project,
|
||||||
"cid:deployEnv" => s.deploy_env,
|
"cid:deployEnv" => server.deploy_env,
|
||||||
"cid:user" => s.created_by,
|
"cid:user" => server.created_by,
|
||||||
"cid:remoteUser" => s.remote_user
|
"cid:remoteUser" => server.remote_user
|
||||||
}
|
}
|
||||||
compute.create_tags(s.id, tags)
|
|
||||||
tags
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_server s
|
def delete_server s
|
||||||
@ -231,18 +231,11 @@ module Provider
|
|||||||
'TemplateBody' => stack.template_body,
|
'TemplateBody' => stack.template_body,
|
||||||
'Parameters' => stack.parameters || {},
|
'Parameters' => stack.parameters || {},
|
||||||
'Capabilities' => ['CAPABILITY_IAM'],
|
'Capabilities' => ['CAPABILITY_IAM'],
|
||||||
'Tags' => {
|
'Tags' => stack_tags(stack)
|
||||||
"StackName" => stack.name,
|
|
||||||
"StackTemplate" => stack.stack_template,
|
|
||||||
"cid:project" => stack.project,
|
|
||||||
"cid:deployEnv" => stack.deploy_env,
|
|
||||||
"cid:user" => stack.owner
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
stack.id = response.body['StackId']
|
stack.id = response.body['StackId']
|
||||||
out << "Stack id: #{stack.id}\n"
|
out << "Stack id: #{stack.id}\n"
|
||||||
#set_stack_tags(stack, out)
|
|
||||||
out.flush
|
out.flush
|
||||||
rescue Excon::Errors::Conflict => e
|
rescue Excon::Errors::Conflict => e
|
||||||
raise ProviderErrors::NameConflict
|
raise ProviderErrors::NameConflict
|
||||||
@ -260,20 +253,17 @@ module Provider
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_stack_tags stack, out=""
|
def stack_tags stack
|
||||||
tags = {
|
{
|
||||||
# "cid:remoteUser" => s.remote_user
|
"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']}
|
end
|
||||||
#ids << stack.id
|
|
||||||
#compute.create_tags(ids, tags)
|
def update_stack(stack, params)
|
||||||
stack_resources(stack).each do |resource|
|
cloud_formation.update_stack(stack.name, params)
|
||||||
begin
|
|
||||||
compute.create_tags(resource['PhysicalResourceId'], tags)
|
|
||||||
rescue Fog::Compute::AWS::Error => e
|
|
||||||
out << "Error: " + e.message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_stack_template template
|
def validate_stack_template template
|
||||||
@ -294,6 +284,10 @@ module Provider
|
|||||||
cloud_formation.describe_stack_resources({'StackName' => stack.name}).body['StackResources']
|
cloud_formation.describe_stack_resources({'StackName' => stack.name}).body['StackResources']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stack_events(stack)
|
||||||
|
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
|
||||||
|
|
||||||
# не работает, не используется
|
# не работает, не используется
|
||||||
# def stack_resource(stack, resource_id)
|
# def stack_resource(stack, resource_id)
|
||||||
# physical_id = fog_stack(stack).resources.get(resource_id).physical_resource_id
|
# physical_id = fog_stack(stack).resources.get(resource_id).physical_resource_id
|
||||||
|
|||||||
@ -39,7 +39,7 @@ module Provider
|
|||||||
o = Provider.const_get(p.capitalize).new(conf)
|
o = Provider.const_get(p.capitalize).new(conf)
|
||||||
if o.configured?
|
if o.configured?
|
||||||
@@providers[p] = o
|
@@providers[p] = o
|
||||||
@@available_providers << p
|
ProviderFactory.add_provider p
|
||||||
puts "Provider '#{p}' has been loaded"
|
puts "Provider '#{p}' has been loaded"
|
||||||
end
|
end
|
||||||
factory = Provider.const_get(p.capitalize + "AccountsFactory").new
|
factory = Provider.const_get(p.capitalize + "AccountsFactory").new
|
||||||
|
|||||||
28
devops-service/spec/factories/deploy_env.rb
Normal file
28
devops-service/spec/factories/deploy_env.rb
Normal file
@ -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
|
||||||
10
devops-service/spec/factories/image.rb
Normal file
10
devops-service/spec/factories/image.rb
Normal file
@ -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
|
||||||
8
devops-service/spec/factories/key.rb
Normal file
8
devops-service/spec/factories/key.rb
Normal file
@ -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
|
||||||
20
devops-service/spec/factories/report.rb
Normal file
20
devops-service/spec/factories/report.rb
Normal file
@ -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
|
||||||
18
devops-service/spec/factories/server.rb
Normal file
18
devops-service/spec/factories/server.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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', 'date' => Time.now, 'user' => 'root' })
|
||||||
|
end
|
||||||
|
end
|
||||||
24
devops-service/spec/factories/stack.rb
Normal file
24
devops-service/spec/factories/stack.rb
Normal file
@ -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
|
||||||
20
devops-service/spec/factories/stack_template.rb
Normal file
20
devops-service/spec/factories/stack_template.rb
Normal file
@ -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
|
||||||
9
devops-service/spec/factories/user.rb
Normal file
9
devops-service/spec/factories/user.rb
Normal file
@ -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
|
||||||
60
devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb
Normal file
60
devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
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 '.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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
47
devops-service/spec/models/image_spec.rb
Normal file
47
devops-service/spec/models/image_spec.rb
Normal file
@ -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
|
||||||
28
devops-service/spec/models/key_spec.rb
Normal file
28
devops-service/spec/models/key_spec.rb
Normal file
@ -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
|
||||||
43
devops-service/spec/models/report_spec.rb
Normal file
43
devops-service/spec/models/report_spec.rb
Normal file
@ -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
|
||||||
95
devops-service/spec/models/server_spec.rb
Normal file
95
devops-service/spec/models/server_spec.rb
Normal file
@ -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
|
||||||
|
))
|
||||||
|
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.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', '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, 'root')
|
||||||
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'expects string as an argument' do
|
||||||
|
expect {
|
||||||
|
server.set_last_operation(:deploy, 'root')
|
||||||
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
174
devops-service/spec/models/stack/stack_ec2_spec.rb
Normal file
174
devops-service/spec/models/stack/stack_ec2_spec.rb
Normal file
@ -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
|
||||||
|
|
||||||
@ -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
|
||||||
@ -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
|
||||||
107
devops-service/spec/models/user_spec.rb
Normal file
107
devops-service/spec/models/user_spec.rb
Normal file
@ -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
|
||||||
78
devops-service/spec/spec_helper.rb
Normal file
78
devops-service/spec/spec_helper.rb
Normal file
@ -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
|
||||||
0
devops-service/spec/support/blank_file
Normal file
0
devops-service/spec/support/blank_file
Normal file
38
devops-service/spec/support/shared_cloud_deploy_env_specs.rb
Normal file
38
devops-service/spec/support/shared_cloud_deploy_env_specs.rb
Normal file
@ -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
|
||||||
211
devops-service/spec/support/shared_deploy_env_specs.rb
Normal file
211
devops-service/spec/support/shared_deploy_env_specs.rb
Normal file
@ -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
|
||||||
53
devops-service/spec/support/shared_stack_template_specs.rb
Normal file
53
devops-service/spec/support/shared_stack_template_specs.rb
Normal file
@ -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
|
||||||
121
devops-service/spec/support/shared_validation_specs.rb
Normal file
121
devops-service/spec/support/shared_validation_specs.rb
Normal file
@ -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
|
||||||
11
devops-service/spec/support/spec_support.rb
Normal file
11
devops-service/spec/support/spec_support.rb
Normal file
@ -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
|
||||||
@ -21,7 +21,7 @@ class BootstrapWorker < Worker
|
|||||||
bootstrap_template: bootstrap_template
|
bootstrap_template: bootstrap_template
|
||||||
}
|
}
|
||||||
=end
|
=end
|
||||||
executor = Devops::Executor::ServerExecutor.new(server, out)
|
executor = Devops::Executor::ServerExecutor.new(server, out, current_user: owner)
|
||||||
executor.report = report
|
executor.report = report
|
||||||
status = executor.two_phase_bootstrap(options)
|
status = executor.two_phase_bootstrap(options)
|
||||||
mongo.set_report_server_data(jid, server.chef_node_name, server.public_ip || server.private_ip)
|
mongo.set_report_server_data(jid, server.chef_node_name, server.public_ip || server.private_ip)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ class DeployWorker < Worker
|
|||||||
server = Devops::Model::Server.new(server_attrs)
|
server = Devops::Model::Server.new(server_attrs)
|
||||||
report = save_report(file, owner, server)
|
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.report = report
|
||||||
executor.deploy_server_with_tags(tags, deploy_info)
|
executor.deploy_server_with_tags(tags, deploy_info)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -22,34 +22,35 @@ class StackBootstrapWorker < Worker
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
stack = create_stack(stack_attrs)
|
stack = create_stack(stack_attrs)
|
||||||
rescue StackCreatingError
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
#TODO: errors
|
#TODO: errors
|
||||||
begin
|
begin
|
||||||
servers_with_priority = persist_stack_servers!(stack)
|
servers_with_priority = persist_stack_servers!(stack)
|
||||||
unless without_bootstrap
|
unless without_bootstrap
|
||||||
sorted_keys = servers_with_priority.keys.sort{|x,y| y <=> x}
|
sorted_keys = servers_with_priority.keys.sort{|x,y| y <=> x}
|
||||||
sorted_keys.each do |key|
|
sorted_keys.each do |key|
|
||||||
@out.puts "Servers with priority '#{key}':"
|
@out.puts "Servers with priority '#{key}':"
|
||||||
bootstrap_servers!(servers_with_priority[key], report)
|
bootstrap_servers!(servers_with_priority[key], report)
|
||||||
|
end
|
||||||
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
|
end
|
||||||
@out.puts "Done."
|
rescue StackCreatingError
|
||||||
0
|
@out.puts "Stack creating error"
|
||||||
rescue BootstrapingStackServerError
|
|
||||||
@out.puts "\nAn error occured during bootstraping stack servers. Initiating stack rollback."
|
|
||||||
rollback_stack!(stack)
|
|
||||||
1
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -119,7 +119,7 @@ class Worker
|
|||||||
end
|
end
|
||||||
|
|
||||||
def update_job_status(status, job_result_code)
|
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)
|
mongo.set_report_status(jid, status, job_result_code)
|
||||||
status
|
status
|
||||||
end
|
end
|
||||||
@ -130,8 +130,4 @@ class Worker
|
|||||||
config
|
config
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_status id, status
|
|
||||||
self.class.set_status(id, status)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user