From c671e7ed546da2c7713b5755bf4370cd59a9ce27 Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 12:14:02 +0300 Subject: [PATCH 01/22] #868: vpc support --- devops-service/app/api2/handlers/group.rb | 7 ++++-- devops-service/app/api2/handlers/network.rb | 6 ++++- devops-service/app/api2/handlers/provider.rb | 7 +++++- devops-service/app/api2/parsers/network.rb | 15 +++++++++++++ .../app/api2/parsers/security_groups.rb | 15 +++++++++++++ devops-service/app/api2/routes/group.rb | 4 +++- devops-service/app/api2/routes/network.rb | 3 +++ devops-service/app/api2/routes/provider.rb | 18 ++++++++++++++- .../db/mongo/connectors/provider_accounts.rb | 7 ++++++ .../models/deploy_env/cloud_deploy_env.rb | 14 ------------ .../mongo/models/deploy_env/deploy_env_ec2.rb | 10 +++++++++ devops-service/db/mongo/mongo_connector.rb | 2 +- .../db/validators/field_validators/vpc.rb | 21 ++++++++++++++++++ devops-service/providers/base_provider.rb | 9 ++++++++ devops-service/providers/ec2.rb | 22 +++++++++---------- devops-service/providers/openstack.rb | 8 +++---- devops-service/providers/static.rb | 8 ------- 17 files changed, 131 insertions(+), 45 deletions(-) create mode 100644 devops-service/app/api2/parsers/network.rb create mode 100644 devops-service/app/api2/parsers/security_groups.rb create mode 100644 devops-service/db/validators/field_validators/vpc.rb diff --git a/devops-service/app/api2/handlers/group.rb b/devops-service/app/api2/handlers/group.rb index 82ed2d7..8b44aed 100644 --- a/devops-service/app/api2/handlers/group.rb +++ b/devops-service/app/api2/handlers/group.rb @@ -1,3 +1,4 @@ +require "app/api2/parsers/security_groups" require "providers/provider_factory" require_relative "request_handler" @@ -6,13 +7,15 @@ module Devops module Handler class Group < RequestHandler - # TODO: vpc support for ec2 + set_parser Devops::API2_0::Parser::SecurityGroupsParser + def groups provider groups_with_account(provider, nil) end def groups_with_account provider, account - ::Provider::ProviderFactory.get(provider, account).groups()#params + available_keys = ["vpc-id"] + ::Provider::ProviderFactory.get(provider, account).groups(parser.security_groups.select{|k,v| available_keys.include?(k)}) end end end diff --git a/devops-service/app/api2/handlers/network.rb b/devops-service/app/api2/handlers/network.rb index 25c89ec..24403eb 100644 --- a/devops-service/app/api2/handlers/network.rb +++ b/devops-service/app/api2/handlers/network.rb @@ -1,3 +1,4 @@ +require "app/api2/parsers/network" require "providers/provider_factory" require_relative "request_handler" @@ -6,13 +7,16 @@ module Devops module Handler class Network < RequestHandler + set_parser Devops::API2_0::Parser::NetworkParser + def networks provider networks_with_account provider, nil end def networks_with_account provider, account p = ::Provider::ProviderFactory.get(provider, account) - p.networks_detail + available_keys = ["vpc-id"] + p.networks_detail(parser.networks.select{|k,v| available_keys.include?(k)}) end end end diff --git a/devops-service/app/api2/handlers/provider.rb b/devops-service/app/api2/handlers/provider.rb index a054f2c..8e5445d 100644 --- a/devops-service/app/api2/handlers/provider.rb +++ b/devops-service/app/api2/handlers/provider.rb @@ -33,12 +33,17 @@ module Devops end def delete_account name, provider - account = Devops::Db.connector.provider_accounts_show(name) + account = Devops::Db.connector.provider_account(provider, name) Devops::Db.connector.provider_accounts_delete(name) ::Provider::ProviderFactory.delete_account(provider, account) account.to_hash end + def account_vpcs provider, name + Devops::Db.connector.provider_account(provider, name) + ::Provider::ProviderFactory.get(provider, name).describe_vpcs + end + end end end diff --git a/devops-service/app/api2/parsers/network.rb b/devops-service/app/api2/parsers/network.rb new file mode 100644 index 0000000..e808cc3 --- /dev/null +++ b/devops-service/app/api2/parsers/network.rb @@ -0,0 +1,15 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class NetworkParser < RequestParser + + def networks + @params + end + end + end + end +end + diff --git a/devops-service/app/api2/parsers/security_groups.rb b/devops-service/app/api2/parsers/security_groups.rb new file mode 100644 index 0000000..80cb019 --- /dev/null +++ b/devops-service/app/api2/parsers/security_groups.rb @@ -0,0 +1,15 @@ +require_relative "request_parser" + +module Devops + module API2_0 + module Parser + class SecurityGroupsParser < RequestParser + + def security_groups + @params + end + end + end + end +end + diff --git a/devops-service/app/api2/routes/group.rb b/devops-service/app/api2/routes/group.rb index 989c4fb..182d82f 100644 --- a/devops-service/app/api2/routes/group.rb +++ b/devops-service/app/api2/routes/group.rb @@ -13,6 +13,9 @@ module Devops # - headers : # - Accept: application/json # + # * Params: + # vpc-id - string + # # * *Returns* : # - ec2: # { @@ -43,7 +46,6 @@ module Devops # ] # } # } - # TODO: vpc support for ec2 app.get_with_headers "/groups/:provider", :headers => [:accept] do |provider| check_privileges("group", "r") check_provider(provider) diff --git a/devops-service/app/api2/routes/network.rb b/devops-service/app/api2/routes/network.rb index 5da6724..f984438 100644 --- a/devops-service/app/api2/routes/network.rb +++ b/devops-service/app/api2/routes/network.rb @@ -13,6 +13,9 @@ module Devops # - headers : # - Accept: application/json # + # * Params: + # vpc-id - string + # # * *Returns* : array of strings # - ec2: # [ diff --git a/devops-service/app/api2/routes/provider.rb b/devops-service/app/api2/routes/provider.rb index 49a797e..dd51af9 100644 --- a/devops-service/app/api2/routes/provider.rb +++ b/devops-service/app/api2/routes/provider.rb @@ -115,7 +115,23 @@ module Devops app.delete_with_headers "/provider/:provider/account/:account_name", :headers => [:accept, :content_type] do |provider, account_name| check_privileges("provider", "w") check_provider(provider) - create_response("Deleted", {:account => Devops::API2_0::Handler::Provider.new(request).delete_account(provider)}) + create_response("Deleted", {:account => Devops::API2_0::Handler::Provider.new(request).delete_account(account_name, provider)}) + end + + # Describe vpc for account with name :account_name for provider ec2 + # + # * *Request* + # - method : GET + # - headers : + # - Accept: application/json + # - Content-Type: application/json + # + # * *Returns* : 200 + app.get_with_headers "/provider/ec2/account/:account_name/vpcs", :headers => [:accept, :content_type] do |account_name| + provider = "ec2" + check_privileges("provider", "r") + check_provider(provider) + json Devops::API2_0::Handler::Provider.new(request).account_vpcs(provider, account_name) end puts "Provider routes initialized" diff --git a/devops-service/db/mongo/connectors/provider_accounts.rb b/devops-service/db/mongo/connectors/provider_accounts.rb index 3627562..4bd235a 100644 --- a/devops-service/db/mongo/connectors/provider_accounts.rb +++ b/devops-service/db/mongo/connectors/provider_accounts.rb @@ -13,6 +13,13 @@ module Connectors collection.find({provider: provider}).to_a.map{|bson| c.build_from_bson(bson)} end + def provider_account provider, account + c = Provider::ProviderFactory.get_account_class(provider) + bson = collection.find({provider: provider, _id: account}).to_a.first + raise RecordNotFound.new("'Account #{account}' for provider '#{provider}' not found") unless bson + c.build_from_bson(bson) + end + def collection_name 'provider_accounts' end diff --git a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb index 30adbf2..dc62b4d 100644 --- a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb +++ b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb @@ -12,21 +12,7 @@ module Devops class CloudDeployEnv < DeployEnvBase attr_accessor :flavor, :image, :subnets, :groups, :stack_template -=begin -@Deprecated - types :identifier => {:type => String, :empty => false}, - :image => {:type => String, :empty => false}, - :flavor => {:type => String, :empty => false}, - :provider => {:type => String, :empty => false}, - :expires => {:type => String, :empty => false, :nil => true}, - :run_list => {:type => Array, :empty => true}, - :users => {:type => Array, :empty => true}, - :subnets => {:type => Array, :empty => true}, - :groups => {:type => Array, :empty => false}, - :stack_template => {:type => String, :empty => false, :nil => true} -=end - #TODO: account validator set_validators ::Validators::DeployEnv::Flavor, ::Validators::DeployEnv::Image, ::Validators::DeployEnv::Groups, diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb index 39a455a..5377a57 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb @@ -4,6 +4,11 @@ module Devops module Model class DeployEnvEc2 < CloudDeployEnv + attr_accessor :vpc_id + + set_field_validators :vpc_id, ::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::Vpc =begin @Deprecated types :identifier => {:type => String, :empty => false}, @@ -34,6 +39,11 @@ module Devops if self.subnets.size > 1 self.subnets = [ self.subnets[0] ] end + self.vpc_id = d["vpc_id"] + end + + def to_hash + super().merge({"vpc_id" => self.vpc_id}) end def self.create hash diff --git a/devops-service/db/mongo/mongo_connector.rb b/devops-service/db/mongo/mongo_connector.rb index bda584e..baa47ff 100644 --- a/devops-service/db/mongo/mongo_connector.rb +++ b/devops-service/db/mongo/mongo_connector.rb @@ -33,7 +33,7 @@ class MongoConnector [:keys, :key, :key_insert, :key_delete] => :keys_connector, [:save_report, :report, :reports, :set_report_status, :set_report_server_data, :add_report_subreports] => :reports_connector, [:insert_statistic, :search_statistic] => :statistics_connector, - [:provider_accounts, :provider_accounts_insert, :provider_accounts_delete, :provider_accounts_show] => :provider_accounts_connector + [:provider_accounts, :provider_accounts_insert, :provider_accounts_delete, :provider_account] => :provider_accounts_connector ) def initialize(db, host, port=27017, user=nil, password=nil) diff --git a/devops-service/db/validators/field_validators/vpc.rb b/devops-service/db/validators/field_validators/vpc.rb new file mode 100644 index 0000000..f932dc6 --- /dev/null +++ b/devops-service/db/validators/field_validators/vpc.rb @@ -0,0 +1,21 @@ +require_relative "base" + +module Validators + module FieldValidator + class Vpc < Base + + def valid? + provider = ::Provider::ProviderFactory.get(@model.provider, @model.provider_account) + vpcs = provider.describe_vpcs + vpcs.keys.include?(@value) + rescue + raise "Invalid provider account '#{@model.provider_account}'" + end + + def message + "Invalid vpc '#{@value}'." + end + end + end +end + diff --git a/devops-service/providers/base_provider.rb b/devops-service/providers/base_provider.rb index bf9ef34..090b5ed 100644 --- a/devops-service/providers/base_provider.rb +++ b/devops-service/providers/base_provider.rb @@ -14,6 +14,15 @@ module Provider "stack_#{self.ssh_key}-#{s.project}-#{s.deploy_env}-#{Time.now.to_i}" end + + def networks filters={} + [] + end + + def groups filters={} + {} + end + protected def connection_compute options Fog::Compute.new( options ) diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 52644f8..a392139 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -51,14 +51,8 @@ module Provider end end - def groups filters=nil - buf = {} - buf = filters.select{|k,v| ["vpc-id"].include?(k)} unless filters.nil? - g = if buf.empty? - self.compute.describe_security_groups - else - self.compute.describe_security_groups(buf) - end + def groups filters={} + g = self.compute.describe_security_groups(filters) convert_groups(g.body["securityGroupInfo"]) end @@ -72,12 +66,12 @@ module Provider end end - def networks_detail - self.networks + def networks_detail filters={} + self.networks(filters) end - def networks - self.compute.describe_subnets.body["subnetSet"].select{|n| n["state"] == "available"}.map do |n| + def networks filters={} + self.compute.describe_subnets(filters).body["subnetSet"].select{|n| n["state"] == "available"}.map do |n| { "cidr" => n["cidrBlock"], "vpcId" => n["vpcId"], @@ -327,6 +321,10 @@ module Provider def create_default_stack_name s "stack-#{self.ssh_key}-#{s.project}-#{s.deploy_env}-#{Time.now.to_i}".gsub('_', '-') end + + def describe_vpcs + self.compute.describe_vpcs.body["vpcSet"].select{|v| v["state"] == "available"}.map{|v| {"vpc_id" => v["vpcId"], "cidr" => v["cidrBlock"] } } + end private def convert_groups list diff --git a/devops-service/providers/openstack.rb b/devops-service/providers/openstack.rb index 6e8249e..d4807e9 100644 --- a/devops-service/providers/openstack.rb +++ b/devops-service/providers/openstack.rb @@ -30,7 +30,7 @@ module Provider PROVIDER end - def groups filter=nil + def groups filters={} convert_groups(compute.list_security_groups.body["security_groups"]) end @@ -55,8 +55,8 @@ module Provider end end - def networks_detail - net = self.network + def networks_detail filters={} + net = self.network(filters) subnets = net.list_subnets.body["subnets"].select{|s| net.current_tenant["id"] == s["tenant_id"]} net.list_networks.body["networks"].select{|n| n["router:external"] == false and n["status"] == "ACTIVE" and net.current_tenant["id"] == n["tenant_id"]}.map{|n| sn = subnets.detect{|s| n["subnets"][0] == s["id"]} @@ -68,7 +68,7 @@ module Provider } end - def networks + def networks filters={} net = self.network net.list_networks.body["networks"].select{|n| n["router:external"] == false and n["status"] == "ACTIVE" and net.current_tenant["id"] == n["tenant_id"]}.map{|n| { diff --git a/devops-service/providers/static.rb b/devops-service/providers/static.rb index 4f1d788..4436670 100644 --- a/devops-service/providers/static.rb +++ b/devops-service/providers/static.rb @@ -23,18 +23,10 @@ module Provider [] end - def groups filter=nil - {} - end - def images filters [] end - def networks - [] - end - def networks_detail self.networks end From 17f7812d65758036ff00c1f3ce973904ba439bde Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 17 Nov 2015 17:38:58 +0300 Subject: [PATCH 02/22] #868: fixed error with networks for static provider --- devops-service/providers/base_provider.rb | 3 +++ devops-service/providers/ec2.rb | 4 ---- devops-service/providers/static.rb | 4 ---- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/devops-service/providers/base_provider.rb b/devops-service/providers/base_provider.rb index 090b5ed..eee8cc1 100644 --- a/devops-service/providers/base_provider.rb +++ b/devops-service/providers/base_provider.rb @@ -14,6 +14,9 @@ module Provider "stack_#{self.ssh_key}-#{s.project}-#{s.deploy_env}-#{Time.now.to_i}" end + def networks_detail filters={} + networks(filters) + end def networks filters={} [] diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index a392139..af5e7fa 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -66,10 +66,6 @@ module Provider end end - def networks_detail filters={} - self.networks(filters) - end - def networks filters={} self.compute.describe_subnets(filters).body["subnetSet"].select{|n| n["state"] == "available"}.map do |n| { diff --git a/devops-service/providers/static.rb b/devops-service/providers/static.rb index 4436670..e3fcb94 100644 --- a/devops-service/providers/static.rb +++ b/devops-service/providers/static.rb @@ -27,10 +27,6 @@ module Provider [] end - def networks_detail - self.networks - end - def servers @@mongo.servers_find({:provider => PROVIDER}).map{|s| s.to_hash} end From e6903adb921a7c710d48367ae265490f4a956175 Mon Sep 17 00:00:00 2001 From: amartynov Date: Thu, 19 Nov 2015 14:09:38 +0300 Subject: [PATCH 03/22] #868: validation order --- .../models/deploy_env/cloud_deploy_env.rb | 22 ++++----- .../models/deploy_env/deploy_env_base.rb | 18 +++---- .../mongo/models/deploy_env/deploy_env_ec2.rb | 6 +-- devops-service/db/mongo/models/image.rb | 18 +++---- .../db/mongo/models/model_with_provider.rb | 11 +++-- devops-service/db/mongo/models/mongo_model.rb | 48 ++++++++++++++----- .../provider_accounts/ec2_provider_account.rb | 10 ++-- .../provider_accounts/provider_account.rb | 14 +++--- devops-service/db/mongo/models/user.rb | 24 +++++----- .../db/validators/field_validators/base.rb | 5 +- .../field_validators/provider_account.rb | 17 +++++++ devops-service/providers/accounts_factory.rb | 10 +++- devops-service/providers/ec2.rb | 2 +- 13 files changed, 131 insertions(+), 74 deletions(-) create mode 100644 devops-service/db/validators/field_validators/provider_account.rb diff --git a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb index dc62b4d..a19abcc 100644 --- a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb +++ b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb @@ -11,28 +11,28 @@ module Devops module Model class CloudDeployEnv < DeployEnvBase - attr_accessor :flavor, :image, :subnets, :groups, :stack_template +# attr_accessor :flavor, :image, :subnets, :groups, :stack_template set_validators ::Validators::DeployEnv::Flavor, ::Validators::DeployEnv::Image, ::Validators::DeployEnv::Groups, ::Validators::DeployEnv::StackTemplate # set_validators ::Validators::DeployEnv::CloudParameters - set_field_validators :flavor, ::Validators::FieldValidator::Nil, + set_field_validators :flavor, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::Flavor - set_field_validators :image, ::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::Flavor], order: 2 + set_field_validators :image, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::Image - set_field_validators :subnets, ::Validators::FieldValidator::Nil, - ::Validators::FieldValidator::FieldType::Array + ::Validators::FieldValidator::Image], order: 2 + set_field_validators :subnets, [::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::Array], order: 4 # ::Validators::FieldValidator::Subnets.new - set_field_validators :groups, ::Validators::FieldValidator::Nil, - ::Validators::FieldValidator::FieldType::Array + set_field_validators :groups, [::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::Array], order: 4 # ::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::NotEmpty, + ::Validators::FieldValidator::NotEmpty], order: 2 # ::Validators::FieldValidator::StackTemplate.new def initialize d={} diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb index b5aec77..c832d20 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_base.rb @@ -8,27 +8,27 @@ module Devops include ModelWithProvider - attr_accessor :identifier, :run_list, :expires, :users +# attr_accessor :identifier, :run_list, :expires, :users set_validators ::Validators::DeployEnv::RunList, ::Validators::DeployEnv::Expiration, ::Validators::DeployEnv::Users - set_field_validators :identifier, ::Validators::FieldValidator::NotNil, + set_field_validators :identifier, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::Name + ::Validators::FieldValidator::Name] - set_field_validators :run_list, ::Validators::FieldValidator::NotNil, + set_field_validators :run_list, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::Array, - ::Validators::FieldValidator::RunList + ::Validators::FieldValidator::RunList] - set_field_validators :users, ::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::Array + set_field_validators :users, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::Array] - set_field_validators :expires, ::Validators::FieldValidator::Nil, + set_field_validators :expires, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::Expires + ::Validators::FieldValidator::Expires] def initialize d={} self.identifier = d["identifier"] diff --git a/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb b/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb index 5377a57..5e54ba0 100644 --- a/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb +++ b/devops-service/db/mongo/models/deploy_env/deploy_env_ec2.rb @@ -4,11 +4,11 @@ module Devops module Model class DeployEnvEc2 < CloudDeployEnv - attr_accessor :vpc_id +# attr_accessor :vpc_id - set_field_validators :vpc_id, ::Validators::FieldValidator::Nil, + set_field_validators :vpc_id, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::Vpc + ::Validators::FieldValidator::Vpc], order: 3 =begin @Deprecated types :identifier => {:type => String, :empty => false}, diff --git a/devops-service/db/mongo/models/image.rb b/devops-service/db/mongo/models/image.rb index 728a66a..5d68393 100644 --- a/devops-service/db/mongo/models/image.rb +++ b/devops-service/db/mongo/models/image.rb @@ -8,7 +8,7 @@ module Devops class Image < MongoModel include ModelWithProvider - attr_accessor :id, :remote_user, :name, :bootstrap_template +# attr_accessor :id, :remote_user, :name, :bootstrap_template =begin types :id => {:type => String, :empty => false}, :provider => {:type => String, :empty => false}, @@ -20,27 +20,27 @@ module Devops # set_validators ::Validators::Image::ImageInFilter, # ::Validators::Image::BootstrapTemplate - set_field_validators :id, ::Validators::FieldValidator::NotNil, + set_field_validators :id, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, ::Validators::FieldValidator::ImageName, - ::Validators::Image::ImageInFilter + ::Validators::Image::ImageInFilter] - set_field_validators :remote_user, ::Validators::FieldValidator::NotNil, + set_field_validators :remote_user, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::Name + ::Validators::FieldValidator::Name] - set_field_validators :name, ::Validators::FieldValidator::NotNil, + set_field_validators :name, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::ImageName + ::Validators::FieldValidator::ImageName] - set_field_validators :bootstrap_template, ::Validators::FieldValidator::Nil, + set_field_validators :bootstrap_template, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, ::Validators::FieldValidator::Name, - ::Validators::Image::BootstrapTemplate + ::Validators::Image::BootstrapTemplate] def validate! validate_id! diff --git a/devops-service/db/mongo/models/model_with_provider.rb b/devops-service/db/mongo/models/model_with_provider.rb index 850b7ee..b7a5d36 100644 --- a/devops-service/db/mongo/models/model_with_provider.rb +++ b/devops-service/db/mongo/models/model_with_provider.rb @@ -4,14 +4,19 @@ module Devops module Model module ModelWithProvider - attr_accessor :provider, :provider_account +# attr_accessor :provider, :provider_account def ModelWithProvider.included(mod) - mod.set_field_validators :provider, ::Validators::FieldValidator::NotNil, + mod.set_field_validators :provider, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::Provider + ::Validators::FieldValidator::Provider] + + mod.set_field_validators :provider_account, [::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty, + ::Validators::FieldValidator::ProviderAccount], order: 1 end def provider_instance diff --git a/devops-service/db/mongo/models/mongo_model.rb b/devops-service/db/mongo/models/mongo_model.rb index 3721a5c..894da96 100644 --- a/devops-service/db/mongo/models/mongo_model.rb +++ b/devops-service/db/mongo/models/mongo_model.rb @@ -68,15 +68,20 @@ module Devops def validate_fields! result = [] - self.class.field_validators.each do |field, validation_method| - begin - self.send(validation_method) - rescue InvalidRecord => e - result << {key: field, message: e.message} + orders = self.class.field_validators.keys.sort + orders.each do |order| + self.class.field_validators[order].each do |elem| + field = elem[:field] + validation_method = elem[:method] + begin + self.send(validation_method) + rescue InvalidRecord => e + result << {key: field, message: e.message} + end + end + unless result.empty? + raise InvalidRecord.new(error_data: result) end - end - unless result.empty? - raise InvalidRecord.new(error_data: result) end true end @@ -142,6 +147,12 @@ module Devops class << self attr_accessor :validators + + # hash: + # key - (integer) validators order + # value - (hash) validator params: + # field: (string) field name + # method: (string) field validator method attr_accessor :field_validators def inherited(subclass) @@ -159,14 +170,29 @@ module Devops # validate field value # if method validate! returns false, then stop validation without error - def set_field_validators field, *validators + # + # field - (string) field name to validate + # validators - (array) validators list + # options - (hash) options for validator + # order: integer - run validator in order with index order: + def set_field_validators field, validators, options={} + attr_accessor field method_name = "validate_" + field.to_s + "!" define_method(method_name) do validators.each do |validator| - break unless validator.new(self, send(field)).validate! + break unless validator.new(self, field).validate! end end - self.field_validators[field] = method_name + order = options[:order] || 0 + obj = { + field: field, + method: method_name + } + unless self.field_validators[order] + self.field_validators[order] = [obj] + else + self.field_validators[order] << obj + end end # private class methods diff --git a/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb b/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb index 3f2fb41..95117bb 100644 --- a/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb +++ b/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb @@ -4,15 +4,15 @@ module Devops module Model class Ec2ProviderAccount < ProviderAccount - attr_accessor :access_key_id, :availability_zone, :secret_access_key +# attr_accessor :access_key_id, :availability_zone, :secret_access_key - set_field_validators :access_key_id, ::Validators::FieldValidator::NotNil, + set_field_validators :access_key_id, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :secret_access_key, ::Validators::FieldValidator::NotNil, + set_field_validators :secret_access_key, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] def initialize a={} super(a) self.provider = Provider::Ec2::PROVIDER diff --git a/devops-service/db/mongo/models/provider_accounts/provider_account.rb b/devops-service/db/mongo/models/provider_accounts/provider_account.rb index 3a6fb59..d3c9c42 100644 --- a/devops-service/db/mongo/models/provider_accounts/provider_account.rb +++ b/devops-service/db/mongo/models/provider_accounts/provider_account.rb @@ -7,22 +7,22 @@ module Devops include ModelWithProvider - attr_accessor :account_name, :description, :ssh_key +# attr_accessor :account_name, :description, :ssh_key - set_field_validators :account_name, ::Validators::FieldValidator::NotNil, + set_field_validators :account_name, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::Name + ::Validators::FieldValidator::Name] - set_field_validators :description, ::Validators::FieldValidator::Nil, + set_field_validators :description, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::Description + ::Validators::FieldValidator::Description] - set_field_validators :ssh_key, ::Validators::FieldValidator::NotNil, + set_field_validators :ssh_key, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty, - ::Validators::FieldValidator::SshKey + ::Validators::FieldValidator::SshKey] ACCOUNT_FIELDS = { account_name: "Account name (id)", diff --git a/devops-service/db/mongo/models/user.rb b/devops-service/db/mongo/models/user.rb index b16e032..e13b4f1 100644 --- a/devops-service/db/mongo/models/user.rb +++ b/devops-service/db/mongo/models/user.rb @@ -12,20 +12,20 @@ module Devops PRIVILEGES = ["r", "w", "x"] PRIVILEGES_REGEX = /^r?w?x?$/ - attr_accessor :id, :password, :privileges, :email - types :id => {:type => String, :empty => false}, - :email => {:type => String, :empty => false}, - :password => {:type => String, :empty => true} +# 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::Name - set_field_validators :password, ::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::String - set_field_validators :email, ::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::String - set_field_validators :privileges, ::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::Hash + ::Validators::FieldValidator::Name] + set_field_validators :password, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String] + set_field_validators :email, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String] + set_field_validators :privileges, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::Hash] def initialize p={} self.id = p['username'] self.email = p['email'] diff --git a/devops-service/db/validators/field_validators/base.rb b/devops-service/db/validators/field_validators/base.rb index 8c60d57..c76395b 100644 --- a/devops-service/db/validators/field_validators/base.rb +++ b/devops-service/db/validators/field_validators/base.rb @@ -2,9 +2,10 @@ module Validators module FieldValidator class Base - def initialize model, value + def initialize model, field @model = model - @value = value + @field = field + @value = model.send(field) end def validate! diff --git a/devops-service/db/validators/field_validators/provider_account.rb b/devops-service/db/validators/field_validators/provider_account.rb new file mode 100644 index 0000000..3d56074 --- /dev/null +++ b/devops-service/db/validators/field_validators/provider_account.rb @@ -0,0 +1,17 @@ +require_relative "base" + +module Validators + module FieldValidator + class ProviderAccount < Base + + def valid? + accounts = AccountsFactory.accounts(@model.provider) + accounts.map{|a| a.account_name}.include?(@value) + end + + def message + "Account '#{@value}' for provider '#{@model.provider}' does not exist" + end + end + end +end diff --git a/devops-service/providers/accounts_factory.rb b/devops-service/providers/accounts_factory.rb index 7f212e9..07906e1 100644 --- a/devops-service/providers/accounts_factory.rb +++ b/devops-service/providers/accounts_factory.rb @@ -1,6 +1,14 @@ module Provider class AccountsFactory + class << self + + def accounts provider_name + Devops::Db.connector.provider_accounts(provider_name) + end + + end + def init config end @@ -22,7 +30,7 @@ module Provider end def accounts - Devops::Db.connector.provider_accounts(provider_name) + AccountsFactory.accounts(provider_name) end def create_account hash diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index af5e7fa..e593ad6 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -205,7 +205,7 @@ module Provider end def cloud_formation - @cloud_formation ||= Fog::AWS::CloudFormation.new(connection_options) + @cloud_formation ||= Fog::AWS::CloudFormation.new(connection_options) end def create_stack(stack, out) From 96dbbccad1e4f474a6ff381925933878ca41e43f Mon Sep 17 00:00:00 2001 From: amartynov Date: Mon, 30 Nov 2015 12:27:34 +0300 Subject: [PATCH 04/22] stack: servers log info --- devops-service/workers/stack_bootstrap_worker.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/devops-service/workers/stack_bootstrap_worker.rb b/devops-service/workers/stack_bootstrap_worker.rb index c7298b0..6ad25d7 100644 --- a/devops-service/workers/stack_bootstrap_worker.rb +++ b/devops-service/workers/stack_bootstrap_worker.rb @@ -184,6 +184,7 @@ class StackBootstrapWorker < Worker stack_servers_with_priority = {} stack_servers_info.each do |priority, info_array| stack_servers_with_priority[priority] = info_array.map do |extended_info| + @out.puts "Instance '#{extended_info["id"]}' has been launched with stack." server_attrs = { 'provider' => provider.name, 'project' => project.id, From c0846a50daf955e0fbefb755b0485f9c3f8bae7a Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 27 Nov 2015 13:49:41 +0300 Subject: [PATCH 05/22] proper error message --- devops-service/exceptions/invalid_record.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/devops-service/exceptions/invalid_record.rb b/devops-service/exceptions/invalid_record.rb index 48d3750..89fd18d 100644 --- a/devops-service/exceptions/invalid_record.rb +++ b/devops-service/exceptions/invalid_record.rb @@ -1,6 +1,10 @@ require_relative "devops_error" class InvalidRecord < ::Devops::Exception::DevopsError + # message could be a String or a hash like + # { + # error_data: [{:key=>:provider, :message=>"Value can not be undefined"}] + # } def initialize msg if msg.is_a?(String) super(msg) @@ -9,6 +13,17 @@ class InvalidRecord < ::Devops::Exception::DevopsError end end + def message + if @object + messages = @object[:error_data].map do |error_item| + "#{error_item[:key]}: #{error_item[:message]}" + end + "Following errors occured: \n#{messages.join('\n')}" + else + super + end + end + def http_status 400 end From bbb27805a9ad3db4225522db62db271df4aa2db8 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 27 Nov 2015 22:41:47 +0300 Subject: [PATCH 06/22] fix verb in comments --- devops-service/app/api2/routes/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devops-service/app/api2/routes/project.rb b/devops-service/app/api2/routes/project.rb index 7b66b61..62e4b12 100644 --- a/devops-service/app/api2/routes/project.rb +++ b/devops-service/app/api2/routes/project.rb @@ -175,7 +175,7 @@ module Devops # Deletes project servers # # * *Request* - # - method : PATCH + # - method : DELETE # - headers : # - Accept: application/json # - Content-Type: application/json From 4e6e634e4a7b5f7e58664455c536134f3afb14eb Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Mon, 30 Nov 2015 17:56:54 +0400 Subject: [PATCH 07/22] update image specs --- devops-service/spec/models/image_spec.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devops-service/spec/models/image_spec.rb b/devops-service/spec/models/image_spec.rb index be7da26..769e04a 100644 --- a/devops-service/spec/models/image_spec.rb +++ b/devops-service/spec/models/image_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Devops::Model::Image, type: :model do 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', :remote_user, :not_nil, :non_empty_string, :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 @@ -36,6 +36,12 @@ RSpec.describe Devops::Model::Image, type: :model do it 'bootstrap_template should be included in available bootstrap templates' do expect(build(:image, bootstrap_template: 'wrong')).not_to be_valid end + + it 'remote_user should contain only a-zA-Z0-9_-.' do + expect(build(:image, remote_user: 'aA0-.')).to be_valid + expect(build(:image, remote_user: 'name/')).not_to be_valid + expect(build(:image, remote_user: 'name!')).not_to be_valid + end end it '#to_hash_without_id returns provider, name, remote_user and bootstrap_template' do From 98dcbfb9965ecc99f093c96435d153d8bcec5f7c Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Mon, 30 Nov 2015 17:51:43 +0400 Subject: [PATCH 08/22] make report specs local timezone agnostic --- devops-service/spec/models/report_spec.rb | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/devops-service/spec/models/report_spec.rb b/devops-service/spec/models/report_spec.rb index ab06af3..5d03e5b 100644 --- a/devops-service/spec/models/report_spec.rb +++ b/devops-service/spec/models/report_spec.rb @@ -4,20 +4,16 @@ RSpec.describe Devops::Model::Report, type: :model do let(:report) { build(:report) } describe '#initialize' do + let(:given_moment) { Time.new(2007,11,1,15,25,0, "+01:00") } + 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' + converted = build(:report, created_at: given_moment).created_at + expect(converted).to eq given_moment.localtime 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' + converted = build(:report, updated_at: given_moment).updated_at + expect(converted).to eq given_moment.localtime end end From bc1aa956f219ed7c8d0b3692c568e694a49dba48 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Mon, 30 Nov 2015 18:10:35 +0400 Subject: [PATCH 09/22] add rspec junit formatter to Gemfile --- devops-service/Gemfile | 2 +- devops-service/Gemfile.lock | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/devops-service/Gemfile b/devops-service/Gemfile index fe1c3b9..96840ba 100644 --- a/devops-service/Gemfile +++ b/devops-service/Gemfile @@ -13,7 +13,6 @@ gem "chef", ">=12" gem "mongo" gem "bson_ext" gem "multi_json", "1.7.8" -# gem "rufus-scheduler", "2.0.24" gem "sidekiq", "3.2.6" gem 'wisper' gem 'rake', '10.2.0' @@ -31,6 +30,7 @@ group :test do gem 'rspec', '~>3.3' gem 'factory_girl', '~>4.5' gem 'activesupport' + gem 'rspec_junit_formatter' end group :devepoment do diff --git a/devops-service/Gemfile.lock b/devops-service/Gemfile.lock index bac5127..b5bf929 100644 --- a/devops-service/Gemfile.lock +++ b/devops-service/Gemfile.lock @@ -350,6 +350,7 @@ DEPENDENCIES rack-accept-media-types rake (= 10.2.0) rspec (~> 3.3) + rspec_junit_formatter sidekiq (= 3.2.6) sinatra (= 1.4.5) sinatra-contrib @@ -357,6 +358,3 @@ DEPENDENCIES test-unit thin (~> 1.5.1) wisper - -BUNDLED WITH - 1.10.5 From 3610bd2064c5d9fe8b314b21a828c7f4e5af62c4 Mon Sep 17 00:00:00 2001 From: amartynov Date: Tue, 1 Dec 2015 17:16:11 +0300 Subject: [PATCH 10/22] CID-10: added '-' to name values --- devops-service/db/validators/field_validators/name.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devops-service/db/validators/field_validators/name.rb b/devops-service/db/validators/field_validators/name.rb index 4603934..3da7928 100644 --- a/devops-service/db/validators/field_validators/name.rb +++ b/devops-service/db/validators/field_validators/name.rb @@ -4,14 +4,14 @@ module Validators class Name < Base MAX_NAME_LEN = 200 - NAME_REGEX = /\A\w{1,#{MAX_NAME_LEN}}\z/ + NAME_REGEX = /\A[\w\-]{1,#{MAX_NAME_LEN}}\z/ def valid? !NAME_REGEX.match(@value).nil? end def message - "Invalid value '#{@value}': it should contains symbols 'a-zA-Z0-9_' and length should be more then 1 and less or equals then #{MAX_NAME_LEN} symbols" + "Invalid value '#{@value}': it should contains symbols 'a-zA-Z0-9_-' and length should be more then 1 and less or equals then #{MAX_NAME_LEN} symbols" end end end From 83f026e88a8239e616e3d5c0687772429a406041 Mon Sep 17 00:00:00 2001 From: amartynov Date: Thu, 3 Dec 2015 12:18:54 +0300 Subject: [PATCH 11/22] CID-15: fixed --- .../db/mongo/models/provider_accounts/ec2_provider_account.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb b/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb index 95117bb..74e48f6 100644 --- a/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb +++ b/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb @@ -4,7 +4,7 @@ module Devops module Model class Ec2ProviderAccount < ProviderAccount -# attr_accessor :access_key_id, :availability_zone, :secret_access_key + attr_accessor :availability_zone set_field_validators :access_key_id, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, From 2a31ea0b01980086b145b2cdec3c328f2ffbd8c9 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Thu, 3 Dec 2015 10:31:36 +0300 Subject: [PATCH 12/22] give all users permission to create instances in sandboxes --- devops-service/db/mongo/models/project.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devops-service/db/mongo/models/project.rb b/devops-service/db/mongo/models/project.rb index 9f0f568..e34a779 100644 --- a/devops-service/db/mongo/models/project.rb +++ b/devops-service/db/mongo/models/project.rb @@ -93,11 +93,16 @@ module Devops def check_authorization user_id, env e = self.deploy_env(env) return true if user_id == User::ROOT_USER_NAME + return true if is_sandbox? return e.users.include? user_id rescue RecordNotFound => e return false end + def is_sandbox? + id.start_with?('sandbox-') + end + =begin def validate! super From 2ffa331e597c295119892acc0b4f17b2bec2b879 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 4 Dec 2015 12:43:07 +0300 Subject: [PATCH 13/22] update validators in stacks --- .../db/mongo/models/stack/stack_base.rb | 32 +++++++++---------- .../stack_template/stack_template_base.rb | 16 +++++----- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/devops-service/db/mongo/models/stack/stack_base.rb b/devops-service/db/mongo/models/stack/stack_base.rb index a6bea8c..cf118e5 100644 --- a/devops-service/db/mongo/models/stack/stack_base.rb +++ b/devops-service/db/mongo/models/stack/stack_base.rb @@ -19,37 +19,37 @@ module Devops run_list: {type: Array, value_type: String, empty: true, nil: true} # details: {type: Hash, nil: true} # Hash type isn't supported yet - set_field_validators :id, ::Validators::FieldValidator::NotNil, + set_field_validators :id, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :provider, ::Validators::FieldValidator::NotNil, + set_field_validators :provider, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :project, ::Validators::FieldValidator::NotNil, + set_field_validators :project, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :deploy_env, ::Validators::FieldValidator::NotNil, + set_field_validators :deploy_env, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :stack_template, ::Validators::FieldValidator::NotNil, + set_field_validators :stack_template, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :name, ::Validators::FieldValidator::Nil, + set_field_validators :name, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :owner, ::Validators::FieldValidator::NotNil, + set_field_validators :owner, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :run_list, ::Validators::FieldValidator::NotNil, + set_field_validators :run_list, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::Array, - ::Validators::FieldValidator::RunList + ::Validators::FieldValidator::RunList] def initialize attrs={} # self.provider = self.class.provider diff --git a/devops-service/db/mongo/models/stack_template/stack_template_base.rb b/devops-service/db/mongo/models/stack_template/stack_template_base.rb index 6e43166..2bdfb1e 100644 --- a/devops-service/db/mongo/models/stack_template/stack_template_base.rb +++ b/devops-service/db/mongo/models/stack_template/stack_template_base.rb @@ -16,22 +16,22 @@ module Devops template_body: {type: String, empty: false}, owner: {type: String, empty: false} - set_field_validators :id, ::Validators::FieldValidator::NotNil, + set_field_validators :id, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty, + ::Validators::FieldValidator::NotEmpty,] ::Validators::FieldValidator::Name - set_field_validators :provider, ::Validators::FieldValidator::NotNil, + set_field_validators :provider, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :template_body, ::Validators::FieldValidator::NotNil, + set_field_validators :template_body, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] - set_field_validators :owner, ::Validators::FieldValidator::NotNil, + set_field_validators :owner, [::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty + ::Validators::FieldValidator::NotEmpty] set_validators ::Validators::StackTemplate::TemplateContent From e23f78caebec15965a16330e0a000622ab9fd4aa Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Wed, 25 Nov 2015 18:37:47 +0300 Subject: [PATCH 14/22] update field validators in stacks, stack_templates and cloud deploy envs --- .../models/deploy_env/cloud_deploy_env.rb | 3 +-- .../stack_template/stack_template_base.rb | 18 +++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb index a19abcc..c8dbc39 100644 --- a/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb +++ b/devops-service/db/mongo/models/deploy_env/cloud_deploy_env.rb @@ -11,13 +11,12 @@ module Devops module Model class CloudDeployEnv < DeployEnvBase -# attr_accessor :flavor, :image, :subnets, :groups, :stack_template + attr_accessor :flavor, :image, :subnets, :groups, :stack_template set_validators ::Validators::DeployEnv::Flavor, ::Validators::DeployEnv::Image, ::Validators::DeployEnv::Groups, ::Validators::DeployEnv::StackTemplate -# set_validators ::Validators::DeployEnv::CloudParameters set_field_validators :flavor, [::Validators::FieldValidator::Nil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::Flavor], order: 2 diff --git a/devops-service/db/mongo/models/stack_template/stack_template_base.rb b/devops-service/db/mongo/models/stack_template/stack_template_base.rb index 2bdfb1e..58ebc2c 100644 --- a/devops-service/db/mongo/models/stack_template/stack_template_base.rb +++ b/devops-service/db/mongo/models/stack_template/stack_template_base.rb @@ -17,21 +17,21 @@ module Devops owner: {type: String, empty: false} set_field_validators :id, [::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty,] - ::Validators::FieldValidator::Name + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty, + ::Validators::FieldValidator::Name] set_field_validators :provider, [::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] set_field_validators :template_body, [::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] set_field_validators :owner, [::Validators::FieldValidator::NotNil, - ::Validators::FieldValidator::FieldType::String, - ::Validators::FieldValidator::NotEmpty] + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] set_validators ::Validators::StackTemplate::TemplateContent From b65dca60a9e0d37ebc142c037218afcc24360e1a Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Fri, 20 Nov 2015 18:31:54 +0300 Subject: [PATCH 15/22] specs for several connectors --- devops-service/Gemfile | 2 +- devops-service/Gemfile.lock | 2 +- devops-service/db/mongo/connectors/filter.rb | 17 +- .../connectors/helpers/update_command.rb | 14 +- devops-service/db/mongo/connectors/project.rb | 20 +- devops-service/db/mongo/models/project.rb | 21 +- .../stack_template/stack_template_factory.rb | 2 + .../spec/connectors/filter_connector_spec.rb | 90 ++++++ .../spec/connectors/image_connector_spec.rb | 37 +++ .../spec/connectors/key_connector_spec.rb | 62 ++++ .../spec/connectors/project_connector_spec.rb | 295 ++++++++++++++++++ .../connectors/shared_connectors_context.rb | 22 ++ .../stack_template_connector_spec.rb | 42 +++ .../spec/connectors/tester_connector/base.rb | 53 ++++ .../connectors/tester_connector/filter.rb | 6 + .../spec/connectors/tester_connector/image.rb | 6 + .../spec/connectors/tester_connector/key.rb | 6 + .../connectors/tester_connector/project.rb | 6 + .../tester_connector/stack_template.rb | 6 + devops-service/spec/factories/key.rb | 1 + devops-service/spec/factories/project.rb | 12 + devops-service/spec/factories/report.rb | 4 +- .../models/deploy_env/deploy_env_ec2_spec.rb | 14 +- .../deploy_env/deploy_env_openstack_spec.rb | 14 +- devops-service/spec/models/project_spec.rb | 49 +++ devops-service/spec/models/server_spec.rb | 3 +- .../spec/models/shared_models_context.rb | 13 + devops-service/spec/spec_helper.rb | 8 + devops-service/spec/support/array_matcher.rb | 13 + .../spec/support/shared_connectors_specs.rb | 127 ++++++++ devops-service/spec/support/spec_support.rb | 8 + 31 files changed, 909 insertions(+), 66 deletions(-) create mode 100644 devops-service/spec/connectors/filter_connector_spec.rb create mode 100644 devops-service/spec/connectors/image_connector_spec.rb create mode 100644 devops-service/spec/connectors/key_connector_spec.rb create mode 100644 devops-service/spec/connectors/project_connector_spec.rb create mode 100644 devops-service/spec/connectors/shared_connectors_context.rb create mode 100644 devops-service/spec/connectors/stack_template_connector_spec.rb create mode 100644 devops-service/spec/connectors/tester_connector/base.rb create mode 100644 devops-service/spec/connectors/tester_connector/filter.rb create mode 100644 devops-service/spec/connectors/tester_connector/image.rb create mode 100644 devops-service/spec/connectors/tester_connector/key.rb create mode 100644 devops-service/spec/connectors/tester_connector/project.rb create mode 100644 devops-service/spec/connectors/tester_connector/stack_template.rb create mode 100644 devops-service/spec/factories/project.rb create mode 100644 devops-service/spec/models/project_spec.rb create mode 100644 devops-service/spec/models/shared_models_context.rb create mode 100644 devops-service/spec/support/array_matcher.rb create mode 100644 devops-service/spec/support/shared_connectors_specs.rb diff --git a/devops-service/Gemfile b/devops-service/Gemfile index 96840ba..13b88c3 100644 --- a/devops-service/Gemfile +++ b/devops-service/Gemfile @@ -10,7 +10,7 @@ gem "sinatra-websocket"#, "~>0.3.0" gem "fog", "~>1.20" gem "mixlib-shellout" gem "chef", ">=12" -gem "mongo" +gem "mongo", '1.12.3' gem "bson_ext" gem "multi_json", "1.7.8" gem "sidekiq", "3.2.6" diff --git a/devops-service/Gemfile.lock b/devops-service/Gemfile.lock index b5bf929..31da4a0 100644 --- a/devops-service/Gemfile.lock +++ b/devops-service/Gemfile.lock @@ -344,7 +344,7 @@ DEPENDENCIES httpclient mime-types (~> 1.25.1) mixlib-shellout - mongo + mongo (= 1.12.3) multi_json (= 1.7.8) rack (= 1.5.2) rack-accept-media-types diff --git a/devops-service/db/mongo/connectors/filter.rb b/devops-service/db/mongo/connectors/filter.rb index 8fde9c1..3bea129 100644 --- a/devops-service/db/mongo/connectors/filter.rb +++ b/devops-service/db/mongo/connectors/filter.rb @@ -20,23 +20,24 @@ module Connectors f = collection.find('type' => 'image', 'provider' => provider).to_a.first if f.nil? collection.insert('type' => 'image', 'provider' => provider, 'images' => images) - return images + images else f['images'] |= images collection.update({'_id' => f['_id']}, f) - return f['images'] + f['images'] end end def delete_available_images images, provider return unless images.is_a?(Array) - f = collection.find('type' => 'image', 'provider' => provider).to_a.first - unless f.nil? - f['images'] -= images - collection.update({'_id' => f['_id']}, f) - return f['images'] + filter = collection.find('type' => 'image', 'provider' => provider).to_a.first + if filter + filter['images'] -= images + collection.update({'_id' => filter['_id']}, filter) + filter['images'] + else + [] end - [] end end end diff --git a/devops-service/db/mongo/connectors/helpers/update_command.rb b/devops-service/db/mongo/connectors/helpers/update_command.rb index e1ebec7..674eb60 100644 --- a/devops-service/db/mongo/connectors/helpers/update_command.rb +++ b/devops-service/db/mongo/connectors/helpers/update_command.rb @@ -14,16 +14,10 @@ module Connectors end def update(record) - begin - record.validate! - collection.update({"_id" => record.id}, record.to_mongo_hash) - record - rescue Mongo::OperationFailure => e - if e.message =~ /^11000/ - resource_name = StringHelper.underscore_class(record.class) - raise InvalidRecord.new("Duplicate key error: #{resource_name} with id '#{record.id}'") - end - end + record.validate! + r = collection.update({"_id" => record.id}, record.to_mongo_hash) + raise RecordNotFound.new("'#{record.id}' not found") if r['n'] == 0 + record end end diff --git a/devops-service/db/mongo/connectors/project.rb b/devops-service/db/mongo/connectors/project.rb index 4f10fe2..abe74f9 100644 --- a/devops-service/db/mongo/connectors/project.rb +++ b/devops-service/db/mongo/connectors/project.rb @@ -2,9 +2,7 @@ module Connectors class Project < Base include Helpers::InsertCommand, Helpers::ShowCommand, -# Helpers::ListCommand, Helpers::DeleteCommand -# Helpers::UpdateCommand def initialize(db) @@ -73,7 +71,7 @@ module Connectors res.each do |ar| r[ar['_id']] = ar['envs'] end - return r + r end def projects_by_image(image) @@ -98,10 +96,9 @@ module Connectors project end + # {find all projects with deploy_envs with field=value}, {return deploy_envs with field=value} def projects_and_deploy_envs_by_field field, value - q = {} - q[field] = value - # {find all projects with deploy_envs with field=value}, {return deploy_envs with field=value} + q = {field => value} list( {'deploy_envs' => {'$elemMatch' => q}}, {:fields => {'deploy_envs' => {'$elemMatch' => q}}} ) end @@ -114,12 +111,12 @@ module Connectors end def set_project_env_run_list(project_id, env, run_list) - Helpers::RunList.new(run_list).validate! + ::Validators::Helpers::RunList.new(run_list).validate! set_project_deploy_env_field(project_id, env, {"run_list" => run_list}) end - def set_project_run_list(project_id, env, run_list) - Helpers::RunList.new(run_list).validate! + def set_project_run_list(project_id, run_list) + ::Validators::Helpers::RunList.new(run_list).validate! @collection.update({"_id" => project_id}, {"$set" => {run_list: run_list}}) end @@ -128,17 +125,16 @@ module Connectors end def remove_deploy_env_from_project id, env + raise ArgumentError unless env.is_a?(String) @collection.update({"_id" => id}, {'$pull' => {deploy_envs: {identifier: env}} }) end def project_update_field id, field, value - obj = {} - obj[field] = value + obj = {field => value} @collection.update({"_id" => id}, {'$set' => obj }) end def project_update id, params - #raise InvalidRecord.new("You can not change project name for '#{id}'.") if params["name"] keys = %w(run_list description) params.delete_if{|k,v| !keys.include?(k)} @collection.update({"_id" => id}, {'$set' => params }) diff --git a/devops-service/db/mongo/models/project.rb b/devops-service/db/mongo/models/project.rb index e34a779..8b14847 100644 --- a/devops-service/db/mongo/models/project.rb +++ b/devops-service/db/mongo/models/project.rb @@ -23,16 +23,27 @@ module Devops attr_accessor :id, :deploy_envs, :type, :archived, :description, :run_list - types :id => {:type => String, :empty => false}, - :deploy_envs => {:type => Array, :value_type => false, :empty => false}, - :description => {:type => String, :empty => true, :nil => true}, - :run_list => {:type => Array, :value_type => String, :empty => true, :nil => false} - MULTI_TYPE = "multi" + set_field_validators :id, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :deploy_envs, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::Array, + ::Validators::FieldValidator::NotEmpty] + + set_field_validators :description, [::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::String] + + set_field_validators :run_list, [::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::Array, + ::Validators::FieldValidator::RunList] + set_validators ::Validators::DeployEnv::RunList, ::Validators::DeployEnv::DeployEnvs + def self.fields ["deploy_envs", "type", "description"] end diff --git a/devops-service/db/mongo/models/stack_template/stack_template_factory.rb b/devops-service/db/mongo/models/stack_template/stack_template_factory.rb index c085196..2094da3 100644 --- a/devops-service/db/mongo/models/stack_template/stack_template_factory.rb +++ b/devops-service/db/mongo/models/stack_template/stack_template_factory.rb @@ -1,6 +1,8 @@ require_relative "stack_template_base" require_relative "stack_template_openstack" require_relative "stack_template_ec2" +require 'providers/openstack' +require 'providers/ec2' module Devops module Model diff --git a/devops-service/spec/connectors/filter_connector_spec.rb b/devops-service/spec/connectors/filter_connector_spec.rb new file mode 100644 index 0000000..2850835 --- /dev/null +++ b/devops-service/spec/connectors/filter_connector_spec.rb @@ -0,0 +1,90 @@ +require 'db/mongo/connectors/filter' +require 'spec/connectors/tester_connector/filter' +require_relative 'shared_connectors_context' + +RSpec.describe Connectors::Filter, type: :connector do + set_tester_connector TesterConnector::Filter + include_context 'connectors' + let(:provider) {'ec2'} + + describe '#available_images' do + subject { @connector.available_images(provider) } + + context 'when there is no filter for given provider' do + it 'returns empty array' do + expect(subject).to eq [] + end + + context 'when there is filter for given provider but for another type' do + it 'returns empty array' do + test_filter = {provider: provider, type: 'foo', images: ['foo', 'bar']} + @tester_connector.create(test_filter) do + expect(subject).to eq [] + end + end + end + end + + context 'when there is filter for given provider' do + it "returns array with images' names" do + test_filter = {provider: provider, type: 'image', images: ['foo', 'bar']} + @tester_connector.create(test_filter) do + expect(subject).to match_array ['foo', 'bar'] + end + end + end + end + + describe '#add_available_images', cleanup_after: :each do + subject { @connector.add_available_images(['foo'], provider) } + + it "do nothing if images is not Array" do + expect { + result = @connector.add_available_images('foo', provider) + expect(result).to be_nil + }.not_to change {@tester_connector.list} + end + + it 'creates filter if there is no one' do + expect(@tester_connector.list.empty?).to be true + expect(subject).to match_array ['foo'] + expect(@tester_connector.list.empty?).to be false + end + + it 'updates filter if one already exists' do + @tester_connector.create(provider: provider, type: 'image', images: ['bar']) + expect(subject).to match_array ['bar', 'foo'] + expect(@tester_connector.list.first['images']).to match_array ['bar', 'foo'] + end + + it "doesn't duplicate images in filter" do + @tester_connector.create(provider: provider, type: 'image', images: ['foo']) + expect(subject).to match_array ['foo'] + expect(@tester_connector.list.first['images']).to match_array ['foo'] + end + end + + describe '#delete_available_images' do + subject { @connector.delete_available_images(['foo'], provider) } + after { @tester_connector.cleanup } + let(:create_filter) { @tester_connector.create(provider: provider, type: 'image', images: ['foo', 'bar']) } + + it 'does nothing if images is not Array' do + create_filter + expect { + result = @connector.delete_available_images('foo', provider) + expect(result).to be_nil + }.not_to change{ @tester_connector.list.first['images'] } + end + + it 'removes given images from filter' do + create_filter + expect(subject).to match_array ['bar'] + expect(@tester_connector.list.first['images']).to match_array ['bar'] + end + + it 'returns empty array if there is no filter' do + expect(subject).to eq [] + end + end +end diff --git a/devops-service/spec/connectors/image_connector_spec.rb b/devops-service/spec/connectors/image_connector_spec.rb new file mode 100644 index 0000000..585d728 --- /dev/null +++ b/devops-service/spec/connectors/image_connector_spec.rb @@ -0,0 +1,37 @@ +require 'db/mongo/connectors/image' +require 'spec/connectors/tester_connector/image' +require_relative 'shared_connectors_context' + +RSpec.describe Connectors::Image, type: :connector do + set_tester_connector TesterConnector::Image + include_context 'connectors' + let(:model_class) { Devops::Model::Image } + + include_examples 'mongo connector', { + model_name: :image, + only: [:insert, :show, :update, :delete], + field_to_update: :name + } + + describe '#images', cleanup_after: :each do + subject { @connector.images('ec2') } + + it 'should be empty if collection is empty' do + expect(subject).to eq [] + end + + it "returns array of Model::Key's of given provider" do + @tester_connector.create(provider: 'ec2') + @tester_connector.create(provider: 'openstack') + expect(subject).to be_an_array_of(model_class).and have_size(1) + expect(subject.first.provider).to eq 'ec2' + end + + it 'returns images for both providers if is is unset' do + @tester_connector.create(provider: 'ec2') + @tester_connector.create(provider: 'openstack') + expect(@connector.images.length).to eq 2 + end + end + +end diff --git a/devops-service/spec/connectors/key_connector_spec.rb b/devops-service/spec/connectors/key_connector_spec.rb new file mode 100644 index 0000000..9be46ee --- /dev/null +++ b/devops-service/spec/connectors/key_connector_spec.rb @@ -0,0 +1,62 @@ +require 'db/mongo/connectors/key' +require 'db/mongo/models/key' +require 'spec/connectors/tester_connector/key' +require_relative 'shared_connectors_context' + +RSpec.describe Connectors::Key, type: :connector do + set_tester_connector TesterConnector::Key + include_context 'connectors' + let(:model_class) { Devops::Model::Key } + + include_examples 'mongo connector', model_name: :key, only: [:insert, :list] + + describe '#key' do + context 'when scope was passed' do + subject { @connector.key('foo') } + + it 'raises RecordNotFound when there is no such record' do + expect { subject }.to raise_error(RecordNotFound) + end + + it 'returns record if was found' do + @tester_connector.create(id: 'foo') do + expect(subject).to be_an_instance_of(model_class) + end + end + end + + context 'when scope was passed' do + subject { @connector.key('foo', 'user') } + + it 'raises RecordNotFound when there is no such record' do + expect { subject }.to raise_error(RecordNotFound) + end + + it 'returns record if was found' do + @tester_connector.create(id: 'foo', scope: 'user') do + expect(subject).to be_an_instance_of(model_class) + end + end + end + end + + describe '#key_delete' do + subject { @connector.key_delete('foo') } + + it 'raises RecordNotFound if there is no such record' do + expect{ subject }.to raise_error(RecordNotFound) + end + + it 'remove key in "user" scope' do + @tester_connector.create(id: 'foo', scope: 'user') + subject + expect(@tester_connector.list).to eq [] + end + + it "doesn't remove key in 'system' scope" do + @tester_connector.create(id: 'foo', scope: 'system') do + expect {subject}.to raise_error RecordNotFound + end + end + end +end diff --git a/devops-service/spec/connectors/project_connector_spec.rb b/devops-service/spec/connectors/project_connector_spec.rb new file mode 100644 index 0000000..2b86bd8 --- /dev/null +++ b/devops-service/spec/connectors/project_connector_spec.rb @@ -0,0 +1,295 @@ +require 'db/mongo/connectors/project' +require 'spec/connectors/tester_connector/project' +require_relative 'shared_connectors_context' + +RSpec.describe Connectors::Project, type: :connector do + set_tester_connector TesterConnector::Project + include_context 'connectors' + let(:model_class) { Devops::Model::Project } + + include_examples 'mongo connector', { + model_name: :project, + factory_name: :project, + only: [:insert, :show, :delete], + field_to_update: :deploy_envs + } + + describe '#is_project_exists?' do + subject { @connector.is_project_exists?(build(:project, id: 'foo')) } + it 'returns true if project exists' do + @tester_connector.create(id: 'foo') do + expect(subject).to be true + end + end + it 'returns false if project doesn\'t exists' do + expect(subject).to be false + end + end + + describe '#projects_all' do + it 'returns array of projects' do + @tester_connector.create_list(2) do + result = @connector.projects_all + expect(result).to be_an_array_of(model_class).and have_size(2) + end + end + end + + describe '#projects', cleanup_after: :all do + before(:all) do + @tester_connector.create build(:project, id: 'foo', type: 'multi').to_mongo_hash + @tester_connector.create build(:project, id: 'bar', archived: true).to_mongo_hash + @tester_connector.create build(:project, id: 'baz').to_mongo_hash + end + + it 'returns non archived projects with all params unset' do + expect(@connector.projects).to have_size(2) + end + + it 'returns projects with given ids' do + result = @connector.projects(%w(foo baz)) + expect(result).to be_an_array_of(model_class) + expect(result.map(&:id)).to match_array(%w(foo baz)) + end + + it 'returns multi projects if @type == :multi' do + expect( + @connector.projects(nil, :multi).map(&:id) + ).to match_array ['foo'] + end + + it 'returns only given fields and id if @fields is set' do + result = @connector.projects(nil, nil, [:deploy_envs]) + expect(result.map(&:id).compact).not_to eq [] + expect(result.map(&:deploy_envs).compact).not_to eq [] + expect(result.map(&:description).compact).to eq [] + end + + it 'returns only archived projects if @archived=true' do + result = @connector.projects(nil, nil, [], true) + expect(result.map(&:id)).to match_array(%w(bar)) + end + end + + describe '#project_names_with_envs', cleanup_after: :all do + before(:all) do + @tester_connector.create( + id: 'foo', + deploy_envs: [{identifier: 'env1'}, {identifier: 'env2'}] + ) + @tester_connector.create( + id: 'bar', + deploy_envs: [{identifier: 'env3'}, {identifier: 'env4'}] + ) + end + + it 'returns hash like {"project_name" => ["env1", "env2]}' do + result = @connector.project_names_with_envs + expect(result.keys.sort).to match_array %w(foo bar) + expect(result['foo'].sort).to match_array %w(env1 env2) + expect(result['bar'].sort).to match_array %w(env3 env4) + end + + it 'returns only projects with given names' do + expect(@connector.project_names_with_envs(['bar']).keys).to match_array %w(bar) + end + end + + describe '#projects_by_image', cleanup_after: :each do + def env(image) + {image: image, provider: 'ec2'} + end + + it 'returns projects deploy_envs of which have given image' do + @tester_connector.create(id: 'foo', deploy_envs: [env('a'), env('b')]) + @tester_connector.create(id: 'bar', deploy_envs: [env('a'), env('a')]) + @tester_connector.create(id: 'baz', deploy_envs: [env('b'), env('b')]) + result = @connector.projects_by_image('a') + expect(result).to be_an_array_of(model_class) + expect(result.map(&:id)).to match_array %w(foo bar) + end + end + + describe '#projects_by_user', cleanup_after: :each do + def env(users) + {users: users, provider: 'ec2'} + end + + it 'returns projects deploy_envs of which have given user' do + @tester_connector.create(id: 'foo', deploy_envs: [ env(%w(user1 user2)) ]) + @tester_connector.create(id: 'bar', deploy_envs: [ env(%w(user1 user1)) ]) + @tester_connector.create(id: 'baz', deploy_envs: [ env(%w(user2 user2)) ]) + @tester_connector.create(id: 'baf', deploy_envs: [ env(%w(user1)), env(%w(user2)) ]) + result = @connector.projects_by_user('user1') + expect(result).to be_an_array_of(model_class) + expect(result.map(&:id)).to match_array %w(foo bar baf) + end + end + + describe '#archive_project' do + it 'sets archived to true' do + @tester_connector.create(id: 'foo') do + @connector.archive_project('foo') + expect(@tester_connector.show('foo')).to include('archived' => true) + end + end + end + + describe '#unarchive_project' do + it 'unsets archived' do + @tester_connector.create(id: 'foo', archived: true) do + @connector.unarchive_project('foo') + expect(@tester_connector.show('foo')).not_to include('archived') + end + end + end + + describe '#check_project_auth', cleanup_after: :all do + before(:all) do + @tester_connector.create(id: 'foo', deploy_envs: [ + {identifier: 'bar', provider: 'ec2', users: %w(user1)} + ]) + end + + it "raises InvalidPrivileges if given env users don't include given user" do + expect { + @connector.check_project_auth('foo', 'bar', 'user2') + }.to raise_error(InvalidPrivileges) + end + + it "returns project if env's users include given user" do + result = @connector.check_project_auth('foo', 'bar', 'user1') + expect(result).to be_an_instance_of(model_class) + expect(result.id).to eq 'foo' + end + end + + describe '#projects_and_deploy_envs_by_field', cleanup_after: :all do + def env(field, value) + {provider: 'ec2', field => value} + end + before(:all) do + @tester_connector.create(id: 'foo', deploy_envs: [ + env(:image, 'image1'), env(:stack_template, 'template1') + ]) + end + + it 'returns projects with deploy envs containing given field with given value' do + expect( + @connector.projects_and_deploy_envs_by_field(:image, 'image1').map(&:id) + ).to match_array %w(foo) + + expect( + @connector.projects_and_deploy_envs_by_field(:stack_template, 'template1').map(&:id) + ).to match_array %w(foo) + end + end + + describe '#set_project_deploy_env_field', cleanup_after: :each do + it 'updates given env from given field_value_hash' do + @tester_connector.create(id: 'foo', deploy_envs: [ + {identifier: 'bar', provider: 'ec2'} + ]) + @connector.set_project_deploy_env_field('foo', 'bar', image: 'a', stack_template: 'b') + updated_project = @tester_connector.show('foo') + expect(updated_project['deploy_envs'].first['image']).to eq 'a' + expect(updated_project['deploy_envs'].first['stack_template']).to eq 'b' + end + end + + describe '#set_project_env_run_list', cleanup_after: :all do + before(:all) do + @tester_connector.create(id: 'foo', deploy_envs: [{identifier: 'bar', provider: 'ec2'}]) + end + before { allow_any_instance_of(Validators::Helpers::RunList).to receive(:validate!) } + + it 'validates run list' do + run_list = [] + expect_any_instance_of(Validators::Helpers::RunList).to receive(:validate!) + @connector.set_project_env_run_list('foo', 'bar', run_list) + end + + it "updates env's RunList" do + run_list = ['role[foo]'] + @connector.set_project_env_run_list('foo', 'bar', run_list) + expect(@tester_connector.show('foo')['deploy_envs'].first['run_list']).to eq run_list + end + end + + describe '#set_project_env_run_list', cleanup_after: :all do + before(:all) do + @tester_connector.create(id: 'foo') + end + before { allow_any_instance_of(Validators::Helpers::RunList).to receive(:validate!) } + + it 'validates run list' do + run_list = [] + expect_any_instance_of(Validators::Helpers::RunList).to receive(:validate!) + @connector.set_project_run_list('foo', run_list) + end + + it "updates project's RunList" do + run_list = ['role[foo]'] + @connector.set_project_run_list('foo', run_list) + expect(@tester_connector.show('foo')['run_list']).to eq run_list + end + end + + describe '#add_deploy_env_to_project' do + it 'adds env to project' do + @tester_connector.create(id: 'foo', deploy_envs: []) do + @connector.add_deploy_env_to_project('foo', build(:deploy_env)) + expect(@tester_connector.show('foo')['deploy_envs']).not_to be_empty + end + end + end + + describe '#remove_deploy_env_from_project', cleanup_after: :each do + before { @tester_connector.create(id: 'foo', deploy_envs: [{identifier: 'bar'}]) } + + it "removes env from project" do + @connector.remove_deploy_env_from_project('foo', 'bar') + expect(@tester_connector.show('foo')['deploy_envs']).to be_empty + end + + it 'raises ArgumentError if given env is not a String' do + env = build(:deploy_env_ec2, identifier: 'bar') + expect { @connector.remove_deploy_env_from_project('foo', env) }.to raise_error(ArgumentError) + end + end + + describe '#project_update_field', cleanup_after: :each do + before { @tester_connector.create(id: 'foo', description: 'desc', run_list: []) } + subject { @connector.project_update_field('foo', 'run_list', ['role[a]']) } + + it 'updates given field of given project' do + expect {subject}.to change { @tester_connector.show('foo')['run_list'] }.to ['role[a]'] + end + + it "doesn't affect other fields" do + expect {subject}.not_to change { @tester_connector.show('foo')['desc'] } + end + end + + describe '#project_update', cleanup_after: :each do + before { @tester_connector.create(id: 'foo', deploy_envs: [], description: 'desc', run_list: []) } + subject { + attrs = {'id' => 'foo2', 'deploy_envs' => [build(:deploy_env)], 'description' => 'desc2', 'run_list' => ['role[asd]']} + @connector.project_update('foo', attrs) + } + let(:updated_project) { @tester_connector.show('foo') } + + it 'can update run_list and description' do + subject + expect(updated_project['run_list']).to eq ['role[asd]'] + expect(updated_project['description']).to eq 'desc2' + end + + it 'can not update other fields' do + subject + expect(updated_project['_id']).to eq 'foo' + expect(updated_project['deploy_envs']).to eq [] + end + end + +end diff --git a/devops-service/spec/connectors/shared_connectors_context.rb b/devops-service/spec/connectors/shared_connectors_context.rb new file mode 100644 index 0000000..3c0fedb --- /dev/null +++ b/devops-service/spec/connectors/shared_connectors_context.rb @@ -0,0 +1,22 @@ +def set_tester_connector(klass) + define_method :tester_connector_class do + klass + end +end + +RSpec.shared_context 'connectors' do + before(:all) do + @connector = described_class.new(SpecSupport.db) + @tester_connector = tester_connector_class.new + @tester_connector.cleanup + end + after(:all){ @tester_connector.cleanup } +end + +RSpec.shared_context 'cleanup after all', cleanup_after: :all do + after(:all){ @tester_connector.cleanup } +end + +RSpec.shared_context 'cleanup after each', cleanup_after: :each do + after { @tester_connector.cleanup } +end \ No newline at end of file diff --git a/devops-service/spec/connectors/stack_template_connector_spec.rb b/devops-service/spec/connectors/stack_template_connector_spec.rb new file mode 100644 index 0000000..edb9fe7 --- /dev/null +++ b/devops-service/spec/connectors/stack_template_connector_spec.rb @@ -0,0 +1,42 @@ +require 'db/mongo/connectors/stack_template' +require 'spec/connectors/tester_connector/stack_template' +require_relative 'shared_connectors_context' + +RSpec.describe Connectors::StackTemplate, type: :connector do + set_tester_connector TesterConnector::StackTemplate + include_context 'connectors' + let(:model_class) { Devops::Model::StackTemplateEc2 } + + include_examples 'mongo connector', { + model_name: :stack_template, + factory_name: :stack_template_ec2, + only: [:insert, :show, :update, :delete], + field_to_update: :owner + } + + describe '#stack_templates' do + subject { @connector.stack_templates('ec2') } + + it 'should be empty if collection is empty' do + expect(subject).to eq [] + end + + it "returns array of stack_templates of given provider" do + @tester_connector.create(provider: 'ec2') + @tester_connector.create(provider: 'openstack') + expect(subject).to be_a(Array) + expect(subject.length).to eq 1 + expect(subject.first).to be_an_instance_of(model_class) + expect(subject.first.provider).to eq 'ec2' + @tester_connector.cleanup + end + + it 'returns stack_templates for both providers if is is unset' do + @tester_connector.create(provider: 'ec2') + @tester_connector.create(provider: 'openstack') + expect(@connector.stack_templates.length).to eq 2 + @tester_connector.cleanup + end + end + +end diff --git a/devops-service/spec/connectors/tester_connector/base.rb b/devops-service/spec/connectors/tester_connector/base.rb new file mode 100644 index 0000000..4f595ab --- /dev/null +++ b/devops-service/spec/connectors/tester_connector/base.rb @@ -0,0 +1,53 @@ +module TesterConnector + class Base + attr_reader :collection + + def initialize + collection_name = self.class.name.demodulize.underscore.pluralize + @collection = SpecSupport.db.collection(collection_name) + @next_id = 1 + end + + def create(hash={}) + collection.insert(create_params(hash)) + if block_given? + yield + cleanup + end + end + + def create_list(size=2, hash={}) + size.times { create(hash) } + if block_given? + yield + cleanup + end + end + + def list + collection.find().to_a + end + + def show(id) + collection.find({'_id' => id}).to_a.first + end + + def cleanup + collection.remove + end + + private + + def create_params(hash) + params = hash.dup + if params[:id] + params['_id'] = params.delete(:id) + end + unless params['_id'] + params['_id'] = @next_id + @next_id += 1 + end + params + end + end +end \ No newline at end of file diff --git a/devops-service/spec/connectors/tester_connector/filter.rb b/devops-service/spec/connectors/tester_connector/filter.rb new file mode 100644 index 0000000..c8cd04f --- /dev/null +++ b/devops-service/spec/connectors/tester_connector/filter.rb @@ -0,0 +1,6 @@ +require_relative 'base' + +module TesterConnector + class Filter < Base + end +end \ No newline at end of file diff --git a/devops-service/spec/connectors/tester_connector/image.rb b/devops-service/spec/connectors/tester_connector/image.rb new file mode 100644 index 0000000..5f7ac86 --- /dev/null +++ b/devops-service/spec/connectors/tester_connector/image.rb @@ -0,0 +1,6 @@ +require_relative 'base' + +module TesterConnector + class Image < Base + end +end \ No newline at end of file diff --git a/devops-service/spec/connectors/tester_connector/key.rb b/devops-service/spec/connectors/tester_connector/key.rb new file mode 100644 index 0000000..f9dd014 --- /dev/null +++ b/devops-service/spec/connectors/tester_connector/key.rb @@ -0,0 +1,6 @@ +require_relative 'base' + +module TesterConnector + class Key < Base + end +end \ No newline at end of file diff --git a/devops-service/spec/connectors/tester_connector/project.rb b/devops-service/spec/connectors/tester_connector/project.rb new file mode 100644 index 0000000..36f45e2 --- /dev/null +++ b/devops-service/spec/connectors/tester_connector/project.rb @@ -0,0 +1,6 @@ +require_relative 'base' + +module TesterConnector + class Project < Base + end +end \ No newline at end of file diff --git a/devops-service/spec/connectors/tester_connector/stack_template.rb b/devops-service/spec/connectors/tester_connector/stack_template.rb new file mode 100644 index 0000000..38383aa --- /dev/null +++ b/devops-service/spec/connectors/tester_connector/stack_template.rb @@ -0,0 +1,6 @@ +require_relative 'base' + +module TesterConnector + class StackTemplate < Base + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/key.rb b/devops-service/spec/factories/key.rb index 48b0262..06fb96e 100644 --- a/devops-service/spec/factories/key.rb +++ b/devops-service/spec/factories/key.rb @@ -2,6 +2,7 @@ require 'db/mongo/models/key' FactoryGirl.define do factory :key, class: Devops::Model::Key do + id 'user_key' path SpecSupport::BLANK_FILE scope 'user' end diff --git a/devops-service/spec/factories/project.rb b/devops-service/spec/factories/project.rb new file mode 100644 index 0000000..561085f --- /dev/null +++ b/devops-service/spec/factories/project.rb @@ -0,0 +1,12 @@ +require 'db/mongo/models/project' + +FactoryGirl.define do + factory :project, class: Devops::Model::Project do + id 'my_project' + deploy_envs { + [build(:deploy_env_ec2)] + } + run_list [] + description 'desc' + end +end \ No newline at end of file diff --git a/devops-service/spec/factories/report.rb b/devops-service/spec/factories/report.rb index 70395a5..07711f0 100644 --- a/devops-service/spec/factories/report.rb +++ b/devops-service/spec/factories/report.rb @@ -13,8 +13,6 @@ FactoryGirl.define do updated_at Time.now status 'completed' - initialize_with { - new(attributes.stringify_keys) - } + initialize_with { new(attributes.stringify_keys) } end end \ No newline at end of file diff --git a/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb b/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb index 5b622b9..2d3b2d8 100644 --- a/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb +++ b/devops-service/spec/models/deploy_env/deploy_env_ec2_spec.rb @@ -1,19 +1,9 @@ require 'db/mongo/models/deploy_env/deploy_env_ec2' +require_relative '../shared_models_context' 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 + include_context 'stubbed calls to connector in env validators' it_behaves_like 'deploy env' it_behaves_like 'cloud deploy env' diff --git a/devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb b/devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb index 428241f..08783b6 100644 --- a/devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb +++ b/devops-service/spec/models/deploy_env/deploy_env_openstack_spec.rb @@ -1,19 +1,9 @@ require 'db/mongo/models/deploy_env/deploy_env_openstack' +require_relative '../shared_models_context' 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 + include_context 'stubbed calls to connector in env validators' it_behaves_like 'deploy env' it_behaves_like 'cloud deploy env' diff --git a/devops-service/spec/models/project_spec.rb b/devops-service/spec/models/project_spec.rb new file mode 100644 index 0000000..9b19eb2 --- /dev/null +++ b/devops-service/spec/models/project_spec.rb @@ -0,0 +1,49 @@ +require 'db/mongo/models/project' +require_relative 'shared_models_context' + +RSpec.describe Devops::Model::Project, type: :model do + let(:project) { build(:project) } + include_context 'stubbed calls to connector in env validators' + + describe 'validation rules:' do + include_examples 'field type validation', :id, :not_nil, :non_empty_string + include_examples 'field type validation', :deploy_envs, :not_nil, :non_empty_array + include_examples 'field type validation', :description, :maybe_nil, :maybe_empty_string + include_examples 'field type validation', :run_list, :not_nil, :maybe_empty_array, :run_list + + it "isn't valid when has envs with same identifier" do + project.deploy_envs << build(:deploy_env_ec2) + expect(project).not_to be_valid + end + + it "is valid when all envs have uniq identifiers" do + project.deploy_envs << build(:deploy_env_ec2, identifier: 'new') + expect(project).to be_valid + end + + it "isn't valid when at least one of envs isn't valid" do + project.deploy_envs << build(:deploy_env_ec2, identifier: nil) + expect(project).not_to be_valid + end + end + + describe '.fields' do + subject { described_class.fields } + it { should eq %w(deploy_envs type description) } + end + + describe '#initialize' do + it 'sets @type to generic by default' do + expect(described_class.new.type).to eq 'generic' + end + + it 'sets @archived to false by default' do + expect(described_class.new.archived).to eq false + end + + it 'sets run_list to empty_array by default' do + expect(described_class.new.run_list).to eq [] + end + end + +end \ No newline at end of file diff --git a/devops-service/spec/models/server_spec.rb b/devops-service/spec/models/server_spec.rb index f970df8..d49f328 100644 --- a/devops-service/spec/models/server_spec.rb +++ b/devops-service/spec/models/server_spec.rb @@ -24,8 +24,7 @@ RSpec.describe Devops::Model::Server, type: :model do 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 + include_examples 'field type validation', :run_list, :not_nil, :maybe_empty_array, :run_list end describe '#initialize' do diff --git a/devops-service/spec/models/shared_models_context.rb b/devops-service/spec/models/shared_models_context.rb new file mode 100644 index 0000000..51804e0 --- /dev/null +++ b/devops-service/spec/models/shared_models_context.rb @@ -0,0 +1,13 @@ +RSpec.shared_context 'stubbed calls to connector in env validators' do + before do + allow(Provider::ProviderFactory).to receive(:providers).and_return(%w(ec2 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 +end diff --git a/devops-service/spec/spec_helper.rb b/devops-service/spec/spec_helper.rb index 7552bf9..ac74d92 100644 --- a/devops-service/spec/spec_helper.rb +++ b/devops-service/spec/spec_helper.rb @@ -8,6 +8,10 @@ require 'active_support/inflector' root = File.join(File.dirname(__FILE__), "..") $LOAD_PATH.push root unless $LOAD_PATH.include? root +# suppress output +original_stdout = $stdout +$stdout = File.open(File::NULL, "w") + Dir[("./spec/support/**/*.rb")].each { |f| require f } # Factory girl configuration @@ -21,6 +25,10 @@ FactoryGirl.find_definitions RSpec.configure do |config| config.include FactoryGirl::Syntax::Methods + config.after(:all) do + $stdout = original_stdout + end + # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer. diff --git a/devops-service/spec/support/array_matcher.rb b/devops-service/spec/support/array_matcher.rb new file mode 100644 index 0000000..34f7376 --- /dev/null +++ b/devops-service/spec/support/array_matcher.rb @@ -0,0 +1,13 @@ +RSpec::Matchers.define :be_an_array_of do |klass| + match do |actual| + actual.class == Array && + actual.all? {|item| item.class == klass} + end +end + +RSpec::Matchers.define :have_size do |length| + match { |actual| actual.length == length } + failure_message do |actual| + "expected #{actual} to have size #{expected} but have #{actual.length}" + end +end \ No newline at end of file diff --git a/devops-service/spec/support/shared_connectors_specs.rb b/devops-service/spec/support/shared_connectors_specs.rb new file mode 100644 index 0000000..2bfe99d --- /dev/null +++ b/devops-service/spec/support/shared_connectors_specs.rb @@ -0,0 +1,127 @@ +RSpec.shared_examples 'mongo connector' do |options| + model_name = options.fetch(:model_name) + factory_name = options[:factory_name] || model_name + commands = options.fetch(:only) + + if commands.include?(:insert) + insert_method_name = "#{model_name}_insert" + + describe "##{insert_method_name}" do + let(:model) { build(factory_name) } + subject { @connector.send(insert_method_name, model) } + + before { allow(model).to receive(:validate!).and_return(true) } + after { @tester_connector.cleanup } + + it 'inserts new record' do + expect {subject}.to change {@tester_connector.list.size}.from(0).to(1) + end + + it 'validates inserted record' do + expect(model).to receive(:validate!) + subject + end + + it "doesn't insert nonvalid records" do + allow(model).to receive(:validate!) { raise InvalidRecord.new('') } + expect {subject}.to raise_error(InvalidRecord) + end + + it 'sets created_at of record' do + expect(model.created_at).to be nil + subject + expect(@tester_connector.list.first['created_at']).not_to be nil + end + + it 'raises error if record with such id already exists' do + @tester_connector.create(id: model.id) + expect {subject}.to raise_error(InvalidRecord) + end + end + end + + if commands.include?(:list) + list_method_name = "#{model_name.to_s.pluralize}" + + describe "##{list_method_name}" do + subject { @connector.send(list_method_name) } + + it 'should be empty if collection is empty' do + expect(subject).to eq [] + end + + it "returns array of #{model_name.to_s.pluralize} if collection isn't empty" do + @tester_connector.create_list(2) do + expect(subject).to be_an_array_of(model_class).and have_size(2) + end + end + end + end + + if commands.include?(:show) + show_method_name = model_name + + describe "##{show_method_name}" do + subject { @connector.send(show_method_name, 'foo') } + + it 'raises RecordNotFound when there is no such record' do + expect { subject }.to raise_error(RecordNotFound) + end + + it 'returns record if was found' do + @tester_connector.create(build(factory_name, id: 'foo').to_mongo_hash) do + expect(subject).to be_an_instance_of(model_class) + end + end + end + end + + if commands.include?(:update) + update_method_name = "#{model_name}_update" + field_to_update = options.fetch(:field_to_update).to_s + + describe "##{update_method_name}" do + let(:model) { build(model_name, field_to_update => 'new_value') } + let(:insert_model_to_update) { @tester_connector.create(attributes_for(factory_name)) } + subject { @connector.send(update_method_name, model) } + before { allow(model).to receive(:validate!).and_return(true) } + after { @tester_connector.cleanup } + + it 'validates updated record' do + insert_model_to_update + expect(model).to receive(:validate!) + subject + end + + it 'updates record' do + insert_model_to_update + subject + updated_value = @tester_connector.list.first[field_to_update] + expect(updated_value).to eq 'new_value' + end + + it 'raises RecordNotFound if there is no such record' do + expect{ subject }.to raise_error(RecordNotFound) + end + end + end + + if commands.include?(:delete) + delete_method_name = "#{model_name}_delete" + describe "##{delete_method_name}" do + let(:model) { build(factory_name) } + let(:insert_model_to_delete) { @tester_connector.create(model.to_mongo_hash) } + subject { @connector.send(delete_method_name, model.id) } + + it 'deletes record' do + insert_model_to_delete + expect {subject}.to change {@tester_connector.list.empty?}.to(true) + @tester_connector.cleanup + end + + it 'raises RecordNotFound if there is no such record' do + expect { subject }.to raise_error(RecordNotFound) + end + end + end +end diff --git a/devops-service/spec/support/spec_support.rb b/devops-service/spec/support/spec_support.rb index 159e1cf..2c5a04e 100644 --- a/devops-service/spec/support/spec_support.rb +++ b/devops-service/spec/support/spec_support.rb @@ -2,10 +2,18 @@ require 'core/devops-application' module SpecSupport BLANK_FILE = File.join(Devops::Application.root, 'spec/support/blank_file') + TEST_DB = 'devops_test' 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 + + def self.db + @db ||= begin + require 'mongo' + Mongo::MongoClient.new.db(TEST_DB) + end + end end \ No newline at end of file From 82ad9cac0dedeb3fe9223736c8357357c04d9fb7 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Tue, 1 Dec 2015 17:40:37 +0300 Subject: [PATCH 16/22] add specs config --- devops-service/spec/support/config.yml | 7 +++++ devops-service/spec/support/spec_support.rb | 33 +++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 devops-service/spec/support/config.yml diff --git a/devops-service/spec/support/config.yml b/devops-service/spec/support/config.yml new file mode 100644 index 0000000..fe15e15 --- /dev/null +++ b/devops-service/spec/support/config.yml @@ -0,0 +1,7 @@ +# only :db key is required +mongo: + :db: devops_test + :host: + :port: + :user: + :password: diff --git a/devops-service/spec/support/spec_support.rb b/devops-service/spec/support/spec_support.rb index 2c5a04e..664927f 100644 --- a/devops-service/spec/support/spec_support.rb +++ b/devops-service/spec/support/spec_support.rb @@ -1,8 +1,9 @@ -require 'core/devops-application' +require 'yaml' module SpecSupport - BLANK_FILE = File.join(Devops::Application.root, 'spec/support/blank_file') - TEST_DB = 'devops_test' + ROOT = File.join(__dir__, '../../') + BLANK_FILE = File.join(ROOT, 'spec/support/blank_file') + def stub_loggers allow(DevopsLogger).to receive_message_chain('logger.debug') @@ -10,10 +11,32 @@ module SpecSupport allow(DevopsLogger).to receive_message_chain('logger.error') end - def self.db + def self.db_params @db ||= begin + conf = config['mongo'] + db_name = conf.fetch(:db) + [db_name, conf[:host], conf[:port], conf[:user], conf[:password]] + end + end + + def self.db + unless @db require 'mongo' - Mongo::MongoClient.new.db(TEST_DB) + db_name, host, port, user, password = db_params + @db = MongoClient.new(host, port).db(db_name) + @db.authenticate(user, password) unless user.nil? or password.nil? + end + @db + end + + def self.config + @config ||= begin + config_file = ENV['RSPEC_CONFIG_PATH'] || File.join(ROOT, 'spec/support/config.yml') + if File.exists?(config_file) + YAML.load_file(config_file) + else + raise "There is no config file: '#{config_file}'" + end end end end \ No newline at end of file From 1b66ca88adeab7e10dca29da2ccb9dfa478efafa Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Tue, 1 Dec 2015 19:35:45 +0300 Subject: [PATCH 17/22] update specs for new name validation rules --- devops-service/spec/support/shared_validation_specs.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/devops-service/spec/support/shared_validation_specs.rb b/devops-service/spec/support/shared_validation_specs.rb index 487e8d7..4d86657 100644 --- a/devops-service/spec/support/shared_validation_specs.rb +++ b/devops-service/spec/support/shared_validation_specs.rb @@ -37,7 +37,8 @@ RSpec.shared_examples 'field type validation' do |field, *properties| 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 + expect(build(validated_model_name, field => '!')).not_to be_valid + expect(build(validated_model_name, field => '/')).not_to be_valid end if properties.include?(:only_word_symbols) it 'should contain elements like role[asd] or recipe[asd]' do @@ -99,9 +100,12 @@ RSpec.shared_examples 'field type validation' do |field, *properties| }.not_to raise_error end if properties.include?(:maybe_empty_array) - it 'should contain only word symbols' do + it 'should contain only symbols in [a-zA-Z0-9_-]' do expect{ - build(validated_model_name, field => 'asd-asd').send(field_validation_method) + build(validated_model_name, field => '!').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?(:only_word_symbols) From c320edaa7175566cea547f1b489cfc9979bd7eb3 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Wed, 2 Dec 2015 12:51:23 +0300 Subject: [PATCH 18/22] add few project specs --- devops-service/db/mongo/models/project.rb | 3 +- devops-service/spec/factories/project.rb | 17 ++++- devops-service/spec/models/project_spec.rb | 74 ++++++++++++++++++- .../spec/models/shared_models_context.rb | 8 ++ .../spec/support/shared_deploy_env_specs.rb | 6 +- .../spec/support/shared_validation_specs.rb | 4 +- devops-service/spec/support/spec_support.rb | 7 -- 7 files changed, 100 insertions(+), 19 deletions(-) diff --git a/devops-service/db/mongo/models/project.rb b/devops-service/db/mongo/models/project.rb index 8b14847..da52ce5 100644 --- a/devops-service/db/mongo/models/project.rb +++ b/devops-service/db/mongo/models/project.rb @@ -213,7 +213,8 @@ module Devops end def self.create_roles_response roles - if !roles || roles.is_a?(String) + return "error in creating roles" unless roles + if roles.is_a?(String) roles else info = "" diff --git a/devops-service/spec/factories/project.rb b/devops-service/spec/factories/project.rb index 561085f..4569e4f 100644 --- a/devops-service/spec/factories/project.rb +++ b/devops-service/spec/factories/project.rb @@ -2,11 +2,22 @@ require 'db/mongo/models/project' FactoryGirl.define do factory :project, class: Devops::Model::Project do + transient do + with_deploy_env_identifier false + with_deploy_env_identifiers ['foo'] + end + id 'my_project' - deploy_envs { - [build(:deploy_env_ec2)] - } run_list [] description 'desc' + + after(:build) do |project, evaluator| + unless project.deploy_envs + project.deploy_envs = [] + evaluator.with_deploy_env_identifiers.each do |env_id| + project.deploy_envs << build(:deploy_env_ec2, identifier: env_id) + end + end + end end end \ No newline at end of file diff --git a/devops-service/spec/models/project_spec.rb b/devops-service/spec/models/project_spec.rb index 9b19eb2..c721912 100644 --- a/devops-service/spec/models/project_spec.rb +++ b/devops-service/spec/models/project_spec.rb @@ -12,17 +12,17 @@ RSpec.describe Devops::Model::Project, type: :model do include_examples 'field type validation', :run_list, :not_nil, :maybe_empty_array, :run_list it "isn't valid when has envs with same identifier" do - project.deploy_envs << build(:deploy_env_ec2) + project = build(:project, with_deploy_env_identifiers: %w(foo foo)) expect(project).not_to be_valid end it "is valid when all envs have uniq identifiers" do - project.deploy_envs << build(:deploy_env_ec2, identifier: 'new') + project = build(:project, with_deploy_env_identifiers: %w(foo bar)) expect(project).to be_valid end it "isn't valid when at least one of envs isn't valid" do - project.deploy_envs << build(:deploy_env_ec2, identifier: nil) + project = build(:project, with_deploy_env_identifiers: ['foo', nil]) expect(project).not_to be_valid end end @@ -44,6 +44,74 @@ RSpec.describe Devops::Model::Project, type: :model do it 'sets run_list to empty_array by default' do expect(described_class.new.run_list).to eq [] end + + describe 'deploy envs building' do + let(:params) { {'deploy_envs' => [ {'provider' => @env_provider, 'identifier' => 'foo'} ]} } + + it 'builds envs array from given params' do + @env_provider = 'ec2' + expect( + described_class.new(params).deploy_envs + ).to be_an_array_of(Devops::Model::DeployEnvEc2).and have_size(1) + end + + it 'builds ec2 deploy envs from given params' do + @env_provider = 'ec2' + builded_env = described_class.new(params).deploy_envs.first + expect(builded_env).to be_a(Devops::Model::DeployEnvEc2) + expect(builded_env.identifier).to eq 'foo' + end + + it 'build openstack deploy envs from given params' do + @env_provider = 'openstack' + builded_env = described_class.new(params).deploy_envs.first + expect(builded_env).to be_a(Devops::Model::DeployEnvOpenstack) + end + + it 'build static deploy envs from given params' do + @env_provider = 'static' + builded_env = described_class.new(params).deploy_envs.first + expect(builded_env).to be_a(Devops::Model::DeployEnvStatic) + end + end + end + + describe '#deploy_env' do + let(:project) { project = build(:project, with_deploy_env_identifiers: %w(foo bar)) } + + it 'returns found env' do + expect(project.deploy_env('bar')).to be_an(Devops::Model::DeployEnvEc2) + end + + it 'raises RecordNotFound if there is no such env' do + expect { + project.deploy_env('missing') + }.to raise_error RecordNotFound + end + end + + describe '#add_deploy_env', stub_logger: true do + let(:env) {build(:deploy_env_ec2)} + subject { project.add_deploy_env(env) } + before do + allow(Devops::Db).to receive_message_chain('connector.add_deploy_env_to_project') + end + + it 'inserts deploy env into mongo via connector' do + expect( + Devops::Db + ).to receive_message_chain('connector.add_deploy_env_to_project').with(project.id, env) + subject + end + + it 'creates chef role' do + expect(env).to receive(:create_role).with(project.id) + subject + end + + it 'returns string' do + expect(subject).to be_a(String) + end end end \ No newline at end of file diff --git a/devops-service/spec/models/shared_models_context.rb b/devops-service/spec/models/shared_models_context.rb index 51804e0..fe5e754 100644 --- a/devops-service/spec/models/shared_models_context.rb +++ b/devops-service/spec/models/shared_models_context.rb @@ -11,3 +11,11 @@ RSpec.shared_context 'stubbed calls to connector in env validators' do allow_any_instance_of(Validators::FieldValidator::Image).to receive(:available_images).and_return([{'id' => 'image'}]) end end + +RSpec.shared_context 'stubbed calls to logger', stub_logger: true do + before do + allow(DevopsLogger).to receive_message_chain('logger.debug') + allow(DevopsLogger).to receive_message_chain('logger.info') + allow(DevopsLogger).to receive_message_chain('logger.error') + end +end diff --git a/devops-service/spec/support/shared_deploy_env_specs.rb b/devops-service/spec/support/shared_deploy_env_specs.rb index de65ed3..a23b1b6 100644 --- a/devops-service/spec/support/shared_deploy_env_specs.rb +++ b/devops-service/spec/support/shared_deploy_env_specs.rb @@ -103,10 +103,9 @@ RSpec.shared_examples 'deploy env' do end end - describe '#create_role' do + describe '#create_role', stub_logger: true do subject { env.create_role('project_name') } before do - stub_loggers allow(env).to receive_message_chain('knife_instance.role_name') { 'role_name' } end @@ -153,7 +152,7 @@ RSpec.shared_examples 'deploy env' do end end - describe '#rename' do + describe '#rename', stub_logger: true do subject { env.rename('project_id', 'new_name') } let(:old_role_name) {'project_id_name'} let(:new_role_name) {'project_id_new_name'} @@ -161,7 +160,6 @@ RSpec.shared_examples 'deploy env' do let(:suggested_new_roles) {["role[#{new_role_name}]"]} before do - stub_loggers # simulate correct start conditions env.run_list = suggested_old_roles diff --git a/devops-service/spec/support/shared_validation_specs.rb b/devops-service/spec/support/shared_validation_specs.rb index 4d86657..67bc6a4 100644 --- a/devops-service/spec/support/shared_validation_specs.rb +++ b/devops-service/spec/support/shared_validation_specs.rb @@ -9,7 +9,9 @@ RSpec.shared_examples 'field type validation' do |field, *properties| describe field do it 'should not be nil' do - expect(build(validated_model_name, field => nil)).not_to be_valid + model = build(validated_model_name) + model.send("#{field}=", nil) + expect(model).not_to be_valid end if properties.include?(:not_nil) it 'may be nil' do diff --git a/devops-service/spec/support/spec_support.rb b/devops-service/spec/support/spec_support.rb index 664927f..14a684a 100644 --- a/devops-service/spec/support/spec_support.rb +++ b/devops-service/spec/support/spec_support.rb @@ -4,13 +4,6 @@ module SpecSupport ROOT = File.join(__dir__, '../../') BLANK_FILE = File.join(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 - def self.db_params @db ||= begin conf = config['mongo'] From 1933e25925bbc5eaa111802cfd7685a7a9030420 Mon Sep 17 00:00:00 2001 From: Anton Chuchkalov Date: Wed, 2 Dec 2015 20:29:09 +0300 Subject: [PATCH 19/22] Add specs for several project model methods: add_authorized_user remove_authorized_user check_authorization delete_deploy_env to_hash --- devops-service/db/mongo/models/project.rb | 5 +- devops-service/spec/models/project_spec.rb | 157 +++++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) diff --git a/devops-service/db/mongo/models/project.rb b/devops-service/db/mongo/models/project.rb index da52ce5..a4a4a23 100644 --- a/devops-service/db/mongo/models/project.rb +++ b/devops-service/db/mongo/models/project.rb @@ -81,11 +81,14 @@ module Devops Project.create_roles_response(res) end + # user could be a String or an Array of strings. + # if env is nil, add given user(s) to all project's envs, + # otherwise only to specified one. def add_authorized_user user, env=nil return if user.nil? new_users = ( user.is_a?(Array) ? user : [ user ] ) environments = env.nil? ? self.deploy_envs : [ self.deploy_env(env) ] - environments .each do |e| + environments.each do |e| e.add_users new_users Devops::Db.connector.set_project_deploy_env_field(self.id, e.identifier, {users: e.users}) end diff --git a/devops-service/spec/models/project_spec.rb b/devops-service/spec/models/project_spec.rb index c721912..bed0128 100644 --- a/devops-service/spec/models/project_spec.rb +++ b/devops-service/spec/models/project_spec.rb @@ -114,4 +114,161 @@ RSpec.describe Devops::Model::Project, type: :model do end end + describe '#add_authorized_user' do + before do + allow(Devops::Db).to receive_message_chain('connector.set_project_deploy_env_field') + end + let(:env1) { build(:deploy_env_ec2, identifier: 'foo') } + let(:env2) { build(:deploy_env_ec2, identifier: 'bar') } + let(:project) { build(:project, deploy_envs: [env1, env2]) } + + it 'returns nil if user is nil' do + expect(project.add_authorized_user(nil)).to be_nil + end + + context "when env isn't given" do + context 'when user is a String' do + it 'adds given user to all envs' do + project.add_authorized_user('John') + expect(env1.users).to match_array(%w(root John)) + expect(env2.users).to match_array(%w(root John)) + end + end + + context 'when user is an Array of strings' do + it 'adds given users to all envs' do + project.add_authorized_user(['John', 'Matz']) + expect(env1.users).to match_array(%w(root John Matz)) + expect(env2.users).to match_array(%w(root John Matz)) + end + end + end + + context 'when env is given' do + context 'when user is a String' do + it 'adds given user to given env' do + project.add_authorized_user('John', 'bar') + expect(env1.users).to match_array(%w(root)) + expect(env2.users).to match_array(%w(root John)) + end + end + + context 'when user is an Array of strings' do + it 'adds given users to all envs' do + project.add_authorized_user(['John', 'Matz'], 'bar') + expect(env1.users).to match_array(%w(root)) + expect(env2.users).to match_array(%w(root John Matz)) + end + end + end + end + + describe '#remove_authorized_user' do + before do + allow(Devops::Db).to receive_message_chain('connector.set_project_deploy_env_field') + end + let(:env1) { build(:deploy_env_ec2, identifier: 'foo', users: %w(root John Matz)) } + let(:env2) { build(:deploy_env_ec2, identifier: 'bar', users: %w(root John Matz)) } + let(:project) { build(:project, deploy_envs: [env1, env2]) } + + it 'returns nil if user is nil' do + expect(project.remove_authorized_user(nil)).to be_nil + end + + context "when env isn't given" do + context 'when user is a String' do + it 'adds given user to all envs' do + project.remove_authorized_user('John') + expect(env1.users).to match_array(%w(root Matz)) + expect(env2.users).to match_array(%w(root Matz)) + end + end + + context 'when user is an Array of strings' do + it 'adds given users to all envs' do + project.remove_authorized_user(['John', 'Matz']) + expect(env1.users).to match_array(%w(root)) + expect(env2.users).to match_array(%w(root)) + end + end + end + + context 'when env is given' do + context 'when user is a String' do + it 'adds given user to given env' do + project.remove_authorized_user('John', 'bar') + expect(env1.users).to match_array(%w(root John Matz)) + expect(env2.users).to match_array(%w(root Matz)) + end + end + + context 'when user is an Array of strings' do + it 'adds given users to all envs' do + project.remove_authorized_user(['John', 'Matz'], 'bar') + expect(env1.users).to match_array(%w(root John Matz)) + expect(env2.users).to match_array(%w(root)) + end + end + end + end + + describe '#check_authorization' do + subject { project.check_authorization(@user || 'Matz', 'foo') } + + it 'returns true for root user' do + @user = 'root' + expect(subject).to be true + end + + it "returns false if env's users don't include given user" do + expect(subject).to be false + end + + it "returns true if env's users include given user" do + project.deploy_env('foo').users = %w(root Matz) + expect(subject).to be true + end + + it 'returns false if there is no such env' do + expect(project.check_authorization('root', 'wrong')).to be false + end + end + + describe '#delete_deploy_env' do + it 'removes env' do + allow(Devops::Db).to receive_message_chain('connector.remove_deploy_env_from_project') + expect(Devops::Db).to receive_message_chain('connector.remove_deploy_env_from_project').with(project.id, 'foo') + project.delete_deploy_env('foo') + expect(project.deploy_envs).to match_array [] + end + end + + describe '#to_hash' do + subject { project.to_hash } + it 'returns hash' do + expect(subject).to be_a(Hash) + end + + it 'contains project id under name key' do + expect(subject['name']).to eq project.id + end + + it 'contains deploy_envs converted to hashes' do + expect(subject['deploy_envs']).to be_an_array_of(Hash) + end + + it 'also contains descriptions and run_list' do + expect(subject).to include('description', 'run_list') + end + + it 'contains archived key if project is archived' do + project.archived = true + expect(subject).to include('archived') + end + + it "doesn't contain archived if project isn't archived" do + expect(subject).not_to include('archived') + end + end + end \ No newline at end of file From 47d521870e714b5c75fe99ddbd7d5fae7b915177 Mon Sep 17 00:00:00 2001 From: amartynov Date: Mon, 7 Dec 2015 14:14:10 +0300 Subject: [PATCH 20/22] cid-36 --- devops-service/config.rb | 1 + .../models/provider_accounts/ec2_provider_account.rb | 6 +++++- .../db/validators/field_validators/field_type.rb | 11 +++++++++++ devops-service/providers/ec2.rb | 10 +++++++--- devops-service/providers/ec2_accounts_factory.rb | 1 + 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/devops-service/config.rb b/devops-service/config.rb index 60aaf1a..5bc27b3 100644 --- a/devops-service/config.rb +++ b/devops-service/config.rb @@ -29,6 +29,7 @@ config[:openstack_ssh_key] = "ssh_key" config[:openstack_certificate] = "/path/to/.ssh/openstack.pem" # aws settings +config[:aws_use_iam_profile] = false config[:aws_access_key_id] = "access_key_id" config[:aws_secret_access_key] = "secret_access_key" config[:aws_ssh_key] = "ssh_key" diff --git a/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb b/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb index 3f2fb41..7453835 100644 --- a/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb +++ b/devops-service/db/mongo/models/provider_accounts/ec2_provider_account.rb @@ -4,7 +4,7 @@ module Devops module Model class Ec2ProviderAccount < ProviderAccount - attr_accessor :access_key_id, :availability_zone, :secret_access_key + attr_accessor :availability_zone set_field_validators :access_key_id, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, @@ -13,6 +13,9 @@ module Devops set_field_validators :secret_access_key, ::Validators::FieldValidator::NotNil, ::Validators::FieldValidator::FieldType::String, ::Validators::FieldValidator::NotEmpty + + set_field_validators :use_iam_profile, ::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::Boolean def initialize a={} super(a) self.provider = Provider::Ec2::PROVIDER @@ -45,6 +48,7 @@ module Devops { access_key_id: "AWS account access key", secret_access_key: "AWS account secret key", + use_iam_profile: "Should use iam profile?", availability_zone: "Availability zone, todo: remove field?" }.merge(ProviderAccount::ACCOUNT_FIELDS) end diff --git a/devops-service/db/validators/field_validators/field_type.rb b/devops-service/db/validators/field_validators/field_type.rb index f90d191..2ad69fd 100644 --- a/devops-service/db/validators/field_validators/field_type.rb +++ b/devops-service/db/validators/field_validators/field_type.rb @@ -25,6 +25,17 @@ module Validators end end + class Boolean < FieldType + + def valid? + @value == true or @value == false + end + + def type_name + "boolean" + end + end + class Array < FieldType def type diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index e6d88fb..b001baa 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -15,10 +15,14 @@ module Provider self.certificate_path = config[:aws_certificate] self.ssh_key = config[:aws_ssh_key] options = { - :provider => "aws", - :aws_access_key_id => config[:aws_access_key_id], - :aws_secret_access_key => config[:aws_secret_access_key] + :provider => "aws" } + if config[:aws_use_iam_profile] + options[:use_iam_profile] = true + else + options[:aws_access_key_id] = config[:aws_access_key_id] + options[:aws_secret_access_key] = config[:aws_secret_access_key] + end if config[:aws_proxy] and config[:aws_no_proxy] options[:connection_options] = { :proxy => config[:aws_proxy], diff --git a/devops-service/providers/ec2_accounts_factory.rb b/devops-service/providers/ec2_accounts_factory.rb index 58eb3a8..7a875f3 100644 --- a/devops-service/providers/ec2_accounts_factory.rb +++ b/devops-service/providers/ec2_accounts_factory.rb @@ -20,6 +20,7 @@ module Provider aws_ssh_key: account.ssh_key, aws_access_key_id: account.access_key_id, aws_secret_access_key: account.secret_access_key, + aws_use_iam_profile: account.use_iam_profile, aws_availability_zone: account.availability_zone, aws_proxy: config[:aws_proxy], From 0bf0c6d71348bff40a57ae91156f097833fe35ab Mon Sep 17 00:00:00 2001 From: Anton Martynov Date: Fri, 11 Dec 2015 13:15:36 +0300 Subject: [PATCH 21/22] without aws stack template validation --- devops-service/providers/ec2.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index a51b423..dbb5d6b 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -261,8 +261,9 @@ module Provider end def validate_stack_template template - r = cloud_formation.validate_template({'TemplateBody' => template}) - pp r.body + #r = cloud_formation.validate_template({'TemplateBody' => template}) + #pp r.body + true end def delete_stack(stack) From 0c6d07b60641342d5e05ab8a6c8c046c9b68c10c Mon Sep 17 00:00:00 2001 From: amartynov Date: Mon, 21 Dec 2015 16:07:52 +0300 Subject: [PATCH 22/22] ssh command changed to net/ssh lib --- .../lib/executors/server_executor.rb | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/devops-service/lib/executors/server_executor.rb b/devops-service/lib/executors/server_executor.rb index 7ff7fdc..0518211 100644 --- a/devops-service/lib/executors/server_executor.rb +++ b/devops-service/lib/executors/server_executor.rb @@ -2,6 +2,7 @@ require "lib/knife/knife_factory" require "workers/worker" require "workers/delete_server_worker" require "hooks" +require 'net/ssh' module Devops module Executor @@ -312,15 +313,26 @@ module Devops res = delete_from_chef_server(@server.chef_node_name) begin new_name = "/etc/chef.backup_#{Time.now.strftime("%d-%m-%Y_%H.%M.%S")}" -# r = `ssh -i #{cert_path} -q #{@server.remote_user}@#{@server.private_ip} rm -Rf /etc/chef` - cmd = "ssh -i #{cert_path} -q #{@server.remote_user}@#{@server.private_ip} \"/bin/sh -c 'if [[ -d /etc/chef ]]; then mv /etc/chef #{new_name}; else echo not found; fi'\"" - DevopsLogger.logger.info("Trying to run command '#{cmd}'") - r = `#{cmd}`.strip - if r == 'not found' - res[:server] = "Directory '/etc/chef' does not exists" - else - raise(r) unless $?.success? - res[:server] = "'/etc/chef' renamed to '#{new_name}'" + cmd = (@server.remote_user == 'root' ? "" : "sudo ") + cmd = cmd + "/bin/sh -c 'if [[ -d /etc/chef ]]; then mv /etc/chef #{new_name} && echo ok; else echo not found; fi'" + DevopsLogger.logger.info("SSH: trying to run command '#{cmd}'") + Net::SSH.start(@server.private_ip, @server.remote_user, :keys => [cert_path]) do |session| + session.open_channel do |channel| + channel.request_pty(:modes => { Net::SSH::Connection::Term::ECHO => 0 }) do |c, success| + raise "could not request pty" unless success + channel.exec cmd + channel.on_data do |c_, data| + if data == 'not found' + res[:server] = "Directory '/etc/chef' does not exists" + elsif data == 'ok' + res[:server] = "'/etc/chef' renamed to '#{new_name}'" + else + DevopsLogger.logger.error "Unexpected error: " + data + raise(data) + end + end + end + end end rescue => e DevopsLogger.logger.error "Unbootstrap error: " + e.message