diff --git a/devops-service/app/api2/handlers/provider.rb b/devops-service/app/api2/handlers/provider.rb index 274e840..a054f2c 100644 --- a/devops-service/app/api2/handlers/provider.rb +++ b/devops-service/app/api2/handlers/provider.rb @@ -17,7 +17,7 @@ module Devops end def accounts provider - ::Provider::ProviderFactory.get(provider).accounts + ::Provider::ProviderFactory.get_accounts_factory(provider).accounts end def account_fields provider @@ -25,8 +25,8 @@ module Devops end def add_account provider - account = ::Provider::ProviderFactory.get(provider).create_account(parser.account) - key = Devops::Db.connector.key account.ssh_key + account = ::Provider::ProviderFactory.get_accounts_factory(provider).create_account(parser.account) + account.validate_fields! Devops::Db.connector.provider_accounts_insert(account) ::Provider::ProviderFactory.add_account(provider, account) account.to_hash @@ -35,6 +35,7 @@ module Devops def delete_account name, provider account = Devops::Db.connector.provider_accounts_show(name) Devops::Db.connector.provider_accounts_delete(name) + ::Provider::ProviderFactory.delete_account(provider, account) account.to_hash end diff --git a/devops-service/app/api2/helpers/version_2.rb b/devops-service/app/api2/helpers/version_2.rb index 08a538b..2e4ce94 100644 --- a/devops-service/app/api2/helpers/version_2.rb +++ b/devops-service/app/api2/helpers/version_2.rb @@ -30,7 +30,7 @@ module Devops def check_provider provider list = ::Provider::ProviderFactory.providers - halt_response("Invalid provider '#{provider}', available providers: '#{list.join("', '")}'", 404) unless list.include?(provider) + halt_response("Invalid provider '#{provider}', available providers: '#{list.join("', '")}'", 400) unless list.include?(provider) end # Save information about requests with methods POST, PUT, DELETE diff --git a/devops-service/app/api2/routes/v2.0.rb b/devops-service/app/api2/routes/v2.0.rb index 4a220d9..3645afc 100644 --- a/devops-service/app/api2/routes/v2.0.rb +++ b/devops-service/app/api2/routes/v2.0.rb @@ -61,13 +61,23 @@ module Devops DevopsLogger.logger = @@logger begin res = super(env) - rescue DevopsError => e + rescue ::Devops::Exception::DevopsError => e + return [e.code, {}, e.message] + rescue InvalidRecord => e return [e.code, {}, e.message] end @@access_logger.info(env["REQUEST_METHOD"] + " " + env["REQUEST_URI"] + " - from #{env["HTTP_USER_AGENT"]} (#{env["REMOTE_USER"]}) / #{res[0]}") res end + def handle_exception!(boom) + if boom.is_a?(::Devops::Exception::DevopsError) + boom.http_response + else + super(boom) + end + end + error Devops::ValidationError do e = env["sinatra.error"] #logger.warn e.message @@ -92,12 +102,14 @@ module Devops halt_response(e.message, 404) end +=begin error InvalidRecord do e = env["sinatra.error"] logger.warn e.message logger.warn "Request body: #{request.body.read}" halt_response(e.message, 400) end +=end error InvalidCommand do e = env["sinatra.error"] diff --git a/devops-service/app/devops-api2.rb b/devops-service/app/devops-api2.rb index 4eb8832..5e650de 100644 --- a/devops-service/app/devops-api2.rb +++ b/devops-service/app/devops-api2.rb @@ -78,7 +78,7 @@ module Devops Devops::Api2.register r end Routes.route "/v2.0", Devops::Api2 - Devops::Api2.routes_list + #Devops::Api2.routes_list end private diff --git a/devops-service/db/mongo/models/mongo_model.rb b/devops-service/db/mongo/models/mongo_model.rb index 60e46b7..3721a5c 100644 --- a/devops-service/db/mongo/models/mongo_model.rb +++ b/devops-service/db/mongo/models/mongo_model.rb @@ -66,6 +66,21 @@ module Devops end end + 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} + end + end + unless result.empty? + raise InvalidRecord.new(error_data: result) + end + true + end + def build_error_message(message) # overrided in descendants message @@ -123,17 +138,16 @@ module Devops end @validators = [] -# @field_validators = [] + @field_validators = {} class << self attr_accessor :validators -# attr_accessor :field_validators + attr_accessor :field_validators def inherited(subclass) subclass.validators = [] subclass.validators += self.validators -# subclass.field_validators = [] -# subclass.field_validators += self.field_validators + subclass.field_validators = self.field_validators.clone end # all exceptions are handled in @validate! method @@ -146,11 +160,13 @@ module Devops # validate field value # if method validate! returns false, then stop validation without error def set_field_validators field, *validators - define_method("validate_" + field.to_s + "!") do + method_name = "validate_" + field.to_s + "!" + define_method(method_name) do validators.each do |validator| break unless validator.new(self, send(field)).validate! end end + self.field_validators[field] = method_name 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 85ee83a..3f2fb41 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 @@ -6,6 +6,13 @@ module Devops attr_accessor :access_key_id, :availability_zone, :secret_access_key + set_field_validators :access_key_id, ::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty + + set_field_validators :secret_access_key, ::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::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 2bfed36..3a6fb59 100644 --- a/devops-service/db/mongo/models/provider_accounts/provider_account.rb +++ b/devops-service/db/mongo/models/provider_accounts/provider_account.rb @@ -9,6 +9,21 @@ module Devops attr_accessor :account_name, :description, :ssh_key + set_field_validators :account_name, ::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty, + ::Validators::FieldValidator::Name + + set_field_validators :description, ::Validators::FieldValidator::Nil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty, + ::Validators::FieldValidator::Description + + set_field_validators :ssh_key, ::Validators::FieldValidator::NotNil, + ::Validators::FieldValidator::FieldType::String, + ::Validators::FieldValidator::NotEmpty, + ::Validators::FieldValidator::SshKey + ACCOUNT_FIELDS = { account_name: "Account name (id)", description: "Account description", diff --git a/devops-service/db/validators/field_validators/description.rb b/devops-service/db/validators/field_validators/description.rb new file mode 100644 index 0000000..c414e3a --- /dev/null +++ b/devops-service/db/validators/field_validators/description.rb @@ -0,0 +1,17 @@ +require_relative "base" +module Validators + module FieldValidator + class Description < Base + + MAX_LEN = 500 + + def valid? + @value.size <= 500 + end + + def message + "Invalid value '#{@value}': it should be less or equals then #{MAX_LEN} symbols" + end + end + end +end diff --git a/devops-service/db/validators/field_validators/name.rb b/devops-service/db/validators/field_validators/name.rb index 1319d85..4603934 100644 --- a/devops-service/db/validators/field_validators/name.rb +++ b/devops-service/db/validators/field_validators/name.rb @@ -11,7 +11,7 @@ module Validators 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}" + "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 diff --git a/devops-service/db/validators/field_validators/ssh_key.rb b/devops-service/db/validators/field_validators/ssh_key.rb new file mode 100644 index 0000000..fce76e3 --- /dev/null +++ b/devops-service/db/validators/field_validators/ssh_key.rb @@ -0,0 +1,20 @@ +require_relative "base" +module Validators + module FieldValidator + class SshKey < Base + + MAX_LEN = 500 + + def valid? + Devops::Db.connector.key @value + true + rescue RecordNotFound + false + end + + def message + "Invalid value '#{@value}': ssh key '#{@value}' not found" + end + end + end +end diff --git a/devops-service/exceptions/devops_error.rb b/devops-service/exceptions/devops_error.rb index 996ec04..fdcb6a2 100644 --- a/devops-service/exceptions/devops_error.rb +++ b/devops-service/exceptions/devops_error.rb @@ -1,12 +1,25 @@ +require "json" module Devops module Exception class DevopsError < StandardError - def code + def http_status 500 end + def http_response + [self.http_status, self.http_headers, self.http_body] + end + + def http_body + JSON.pretty_generate(message: self.message) + end + + def http_headers + {"Content-Type" => "application/json"} + end + end end diff --git a/devops-service/exceptions/invalid_record.rb b/devops-service/exceptions/invalid_record.rb index 5ca7cb1..48d3750 100644 --- a/devops-service/exceptions/invalid_record.rb +++ b/devops-service/exceptions/invalid_record.rb @@ -1,3 +1,23 @@ -class InvalidRecord < StandardError +require_relative "devops_error" +class InvalidRecord < ::Devops::Exception::DevopsError + def initialize msg + if msg.is_a?(String) + super(msg) + else + @object = msg + end + end + + def http_status + 400 + end + + def http_body + if @object.nil? + super + else + JSON.pretty_generate(@object) + end + end end diff --git a/devops-service/providers/accounts_factory.rb b/devops-service/providers/accounts_factory.rb index c956b14..7f212e9 100644 --- a/devops-service/providers/accounts_factory.rb +++ b/devops-service/providers/accounts_factory.rb @@ -14,8 +14,20 @@ module Provider @connections[name] = conn end + def delete_connection name + @connections.delete(name) + end + def create_connection_from_account config, account end + + def accounts + Devops::Db.connector.provider_accounts(provider_name) + end + + def create_account hash + raise "override me" + end end end diff --git a/devops-service/providers/base_provider.rb b/devops-service/providers/base_provider.rb index a7d27dc..bf9ef34 100644 --- a/devops-service/providers/base_provider.rb +++ b/devops-service/providers/base_provider.rb @@ -6,14 +6,6 @@ module Provider attr_accessor :ssh_key, :certificate_path, :connection_options, :run_list - def accounts - Devops::Db.connector.provider_accounts(self.name) - end - - def create_account hash - raise "override me" - end - def create_default_chef_node_name s "#{self.ssh_key}-#{s.project}-#{s.deploy_env}-#{Time.now.to_i}" end diff --git a/devops-service/providers/ec2.rb b/devops-service/providers/ec2.rb index 40fb10b..73aff74 100644 --- a/devops-service/providers/ec2.rb +++ b/devops-service/providers/ec2.rb @@ -35,10 +35,6 @@ module Provider super and !(empty_param?(o[:aws_access_key_id]) or empty_param?(o[:aws_secret_access_key])) end - def create_account hash - Devops::Model::Ec2ProviderAccount.new(hash) - end - def name PROVIDER end diff --git a/devops-service/providers/ec2_accounts_factory.rb b/devops-service/providers/ec2_accounts_factory.rb index 88198e2..58eb3a8 100644 --- a/devops-service/providers/ec2_accounts_factory.rb +++ b/devops-service/providers/ec2_accounts_factory.rb @@ -4,13 +4,17 @@ module Provider def init config @connections = {} - Devops::Db.connector.provider_accounts(Ec2::PROVIDER).each do |account| + accounts.each do |account| create_connection_from_account(config, account) puts "\tFound ec2 account '#{account.account_name}'" end ProviderFactory.add_provider Ec2::PROVIDER unless @connections.empty? end + def provider_name + Ec2::PROVIDER + end + def create_connection_from_account config, account options = { aws_ssh_key: account.ssh_key, @@ -25,5 +29,9 @@ module Provider add_connection(account.account_name, Ec2.new(options)) end + def create_account hash + Devops::Model::Ec2ProviderAccount.new(hash) + end + end end diff --git a/devops-service/providers/provider_factory.rb b/devops-service/providers/provider_factory.rb index 3bb8610..921cceb 100644 --- a/devops-service/providers/provider_factory.rb +++ b/devops-service/providers/provider_factory.rb @@ -50,6 +50,7 @@ module Provider next end end + puts "Available providers: #{@@available_providers}" end def self.add_account provider, account @@ -58,6 +59,12 @@ module Provider DevopsLogger.logger.info("Added #{provider} account '#{account.account_name}'") end + def self.delete_account provider, account + factory = @@providers_with_accounts_factories[provider] + factory.delete_connection(account.account_name) + DevopsLogger.logger.info("Removed #{provider} account '#{account.account_name}'") + end + def self.require_all ["ec2", "openstack", "static"].each do |provider| begin @@ -69,6 +76,10 @@ module Provider end end + def self.get_accounts_factory provider + @@providers_with_accounts_factories[provider] + end + def self.get_account_class provider case(provider) when ::Provider::Static::PROVIDER