models in Devops::Model module, auth

This commit is contained in:
Anton Martynov 2015-03-06 12:20:30 +03:00
parent 5ee2e2f714
commit 42f936f040
38 changed files with 1093 additions and 1021 deletions

View File

@ -1,8 +1,9 @@
module Sinatra module Sinatra
module Devops module DevopsAuth
module Helpers module Helpers
def protect! def protect!
return if auth_with_basic? return if auth_with_basic?
headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
halt 401, "Not authorized\n" halt 401, "Not authorized\n"
end end
@ -23,10 +24,10 @@ module Sinatra
end end
def self.registered(app) def self.registered(app)
app.helpers Devops::Helpers app.helpers Sinatra::DevopsAuth::Helpers
end end
end end
register Devops register Sinatra::DevopsAuth
end end

View File

@ -182,7 +182,7 @@ module ServerCommands
servers = [] servers = []
servers_info.each do |info| servers_info.each do |info|
image = info[:image] image = info[:image]
s = Server.new s = Devops::Model::Server.new
s.provider = provider.name s.provider = provider.name
s.project = project_name s.project = project_name
s.deploy_env = env_name s.deploy_env = env_name

View File

@ -20,7 +20,7 @@ module Connectors
private private
def model_from_bson(bson) def model_from_bson(bson)
::Image.build_from_bson(bson) Devops::Model::Image.build_from_bson(bson)
end end
end end

View File

@ -21,7 +21,7 @@ module Connectors
private private
def model_from_bson(bson) def model_from_bson(bson)
::Key.build_from_bson(bson) Devops::Model::Key.build_from_bson(bson)
end end
end end

View File

@ -96,7 +96,7 @@ module Connectors
private private
def model_from_bson(bson) def model_from_bson(bson)
::Project.build_from_bson(bson) Devops::Model::Project.build_from_bson(bson)
end end
end end

View File

@ -41,7 +41,7 @@ module Connectors
private private
def model_from_bson(bson) def model_from_bson(bson)
::Report.new(bson) Devops::Model::Report.new(bson)
end end
end end

View File

@ -66,7 +66,7 @@ module Connectors
private private
def model_from_bson(bson) def model_from_bson(bson)
::Server.build_from_bson(bson) Devops::Model::Server.build_from_bson(bson)
end end
# couldn't be replaced with ShowCommand (_id doesn't neccesary appear in params) # couldn't be replaced with ShowCommand (_id doesn't neccesary appear in params)

View File

@ -18,7 +18,7 @@ module Connectors
def model_from_bson(bson) def model_from_bson(bson)
provider = bson['provider'] provider = bson['provider']
::StackFactory.get_class(provider).build_from_bson(bson) Devops::Model::StackFactory.get_class(provider).build_from_bson(bson)
end end
end end
end end

View File

@ -18,7 +18,7 @@ module Connectors
def model_from_bson(bson) def model_from_bson(bson)
provider = bson['provider'] provider = bson['provider']
::StackTemplateFactory.get_class(provider).build_from_bson(bson) Devops::Model::StackTemplateFactory.get_class(provider).build_from_bson(bson)
end end
end end

View File

@ -29,7 +29,7 @@ module Connectors
def create_root_user def create_root_user
u = user('root') u = user('root')
rescue RecordNotFound => e rescue RecordNotFound => e
root = ::User.create_root root = Devops::Model::User.create_root
collection.insert(root.to_mongo_hash) collection.insert(root.to_mongo_hash)
end end
@ -49,7 +49,7 @@ module Connectors
private private
def model_from_bson(bson) def model_from_bson(bson)
::User.build_from_bson(bson) Devops::Model::User.build_from_bson(bson)
end end
end end

View File

@ -3,54 +3,58 @@ require "db/exceptions/invalid_record"
require "providers/provider_factory" require "providers/provider_factory"
require "commands/deploy_env" require "commands/deploy_env"
class DeployEnvBase < MongoModel module Devops
module Model
class DeployEnvBase < MongoModel
include DeployEnvCommands include DeployEnvCommands
attr_accessor :identifier, :run_list, :expires, :provider, :users attr_accessor :identifier, :run_list, :expires, :provider, :users
def initialize d={} def initialize d={}
self.identifier = d["identifier"] self.identifier = d["identifier"]
b = d["run_list"] || [] b = d["run_list"] || []
self.run_list = (b.is_a?(Array) ? b.uniq : b) self.run_list = (b.is_a?(Array) ? b.uniq : b)
self.expires = d["expires"] self.expires = d["expires"]
self.provider = d["provider"] self.provider = d["provider"]
b = d["users"] || [] b = d["users"] || []
self.users = (b.is_a?(Array) ? b.uniq : b) self.users = (b.is_a?(Array) ? b.uniq : b)
end end
def to_hash def to_hash
{ {
"identifier" => self.identifier, "identifier" => self.identifier,
"run_list" => self.run_list, "run_list" => self.run_list,
"expires" => self.expires, "expires" => self.expires,
"provider" => self.provider, "provider" => self.provider,
"users" => self.users "users" => self.users
} }
end end
def provider_instance def provider_instance
@provider_instance ||= ::Provider::ProviderFactory.get(self.provider) @provider_instance ||= ::Provider::ProviderFactory.get(self.provider)
end end
def build_error_message(message) def build_error_message(message)
"Deploy environment '#{self.identifier}'. " + message "Deploy environment '#{self.identifier}'. " + message
end end
# class methods # class methods
class << self class << self
def validators
@validators
end
private
def set_validators(*validators)
@validators = validators
end
end
def validators
@validators
end end
private
def set_validators(*validators)
@validators = validators
end
end end
end end

View File

@ -1,63 +1,67 @@
require "db/mongo/models/deploy_env/deploy_env_base" require "db/mongo/models/deploy_env/deploy_env_base"
class DeployEnvEc2 < DeployEnvBase module Devops
module Model
class DeployEnvEc2 < DeployEnvBase
attr_accessor :flavor, :image, :subnets, :groups attr_accessor :flavor, :image, :subnets, :groups
types :identifier => {:type => String, :empty => false}, types :identifier => {:type => String, :empty => false},
:image => {:type => String, :empty => false}, :image => {:type => String, :empty => false},
:flavor => {:type => String, :empty => false}, :flavor => {:type => String, :empty => false},
:provider => {:type => String, :empty => false}, :provider => {:type => String, :empty => false},
:expires => {:type => String, :empty => false, :nil => true}, :expires => {:type => String, :empty => false, :nil => true},
:run_list => {:type => Array, :empty => true}, :run_list => {:type => Array, :empty => true},
:users => {:type => Array, :empty => true}, :users => {:type => Array, :empty => true},
:subnets => {:type => Array, :empty => true}, :subnets => {:type => Array, :empty => true},
:groups => {:type => Array, :empty => false} :groups => {:type => Array, :empty => false}
set_validators ::Validators::DeployEnv::RunList, set_validators ::Validators::DeployEnv::RunList,
::Validators::DeployEnv::Expiration, ::Validators::DeployEnv::Expiration,
::Validators::DeployEnv::Users, ::Validators::DeployEnv::Users,
::Validators::DeployEnv::Flavor, ::Validators::DeployEnv::Flavor,
::Validators::DeployEnv::Image, ::Validators::DeployEnv::Image,
::Validators::DeployEnv::SubnetBelongsToProvider, ::Validators::DeployEnv::SubnetBelongsToProvider,
::Validators::DeployEnv::Groups ::Validators::DeployEnv::Groups
def initialize d={} def initialize d={}
super(d) super(d)
self.flavor = d["flavor"] self.flavor = d["flavor"]
self.image = d["image"] self.image = d["image"]
b = d["subnets"] || [] b = d["subnets"] || []
self.subnets = if b.is_a?(Array) self.subnets = if b.is_a?(Array)
(b.size > 1 ? [ b[0] ] : b) (b.size > 1 ? [ b[0] ] : b)
else else
b b
end end
b = d["groups"] || ["default"] b = d["groups"] || ["default"]
self.groups = (b.is_a?(Array) ? b.uniq : b) self.groups = (b.is_a?(Array) ? b.uniq : b)
end end
def to_hash def to_hash
h = super h = super
h.merge!({ h.merge!({
"flavor" => self.flavor, "flavor" => self.flavor,
"image" => self.image, "image" => self.image,
"subnets" => self.subnets, "subnets" => self.subnets,
"groups" => self.groups "groups" => self.groups
}) })
end end
def self.create hash def self.create hash
DeployEnvEc2.new(hash) DeployEnvEc2.new(hash)
end end
private private
def subnets_filter def subnets_filter
networks = provider_instance.networks networks = provider_instance.networks
unless self.subnets.empty?
{"vpc-id" => networks.detect{|n| n["name"] == self.subnets[0]}["vpcId"] }
end
end
unless self.subnets.empty?
{"vpc-id" => networks.detect{|n| n["name"] == self.subnets[0]}["vpcId"] }
end end
end end
end end

View File

@ -2,20 +2,24 @@ require_relative "deploy_env_static"
require_relative "deploy_env_openstack" require_relative "deploy_env_openstack"
require_relative "deploy_env_ec2" require_relative "deploy_env_ec2"
class DeployEnvFactory module Devops
module Model
class DeployEnvFactory
def self.create hash
c = case(hash["provider"])
when ::Provider::Static::PROVIDER
DeployEnvStatic
when ::Provider::Ec2::PROVIDER
DeployEnvEc2
when ::Provider::Openstack::PROVIDER
DeployEnvOpenstack
else
raise InvalidRecord.new "Invalid provider '#{hash["provider"]}' for deploy envirenment '#{hash["identifier"]}'"
end
c.new(hash)
end
def self.create hash
c = case(hash["provider"])
when ::Provider::Static::PROVIDER
DeployEnvStatic
when ::Provider::Ec2::PROVIDER
DeployEnvEc2
when ::Provider::Openstack::PROVIDER
DeployEnvOpenstack
else
raise InvalidRecord.new "Invalid provider '#{hash["provider"]}' for deploy envirenment '#{hash["identifier"]}'"
end end
c.new(hash)
end end
end end

View File

@ -2,117 +2,121 @@ require "db/mongo/models/mongo_model"
require "db/exceptions/invalid_record" require "db/exceptions/invalid_record"
require "commands/deploy_env" require "commands/deploy_env"
class DeployEnvMulti < MongoModel module Devops
module Model
class DeployEnvMulti < MongoModel
include DeployEnvCommands include DeployEnvCommands
attr_accessor :identifier, :servers, :expires, :users attr_accessor :identifier, :servers, :expires, :users
types :identifier => {:type => String, :empty => false}, types :identifier => {:type => String, :empty => false},
:expires => {:type => String, :empty => false, :nil => true}, :expires => {:type => String, :empty => false, :nil => true},
:users => {:type => Array, :empty => true}, :users => {:type => Array, :empty => true},
:servers => {:type => Array, :empty => false, :value_type => Hash} :servers => {:type => Array, :empty => false, :value_type => Hash}
def initialize d={} def initialize d={}
self.identifier = d["identifier"] self.identifier = d["identifier"]
self.expires = d["expires"] self.expires = d["expires"]
self.servers = d["servers"] self.servers = d["servers"]
b = d["users"] || [] b = d["users"] || []
self.users = (b.is_a?(Array) ? b.uniq : b) self.users = (b.is_a?(Array) ? b.uniq : b)
end
def validate!
super
e = []
check_users!(self.users)
unless self.expires.nil?
check_expires!(self.expires)
end
self.servers.each_with_index do |server, i|
begin
if server["priority"].nil?
server["priority"] = 100
else
begin
Integer(server["priority"])
rescue ArgumentError, TypeError
raise InvalidRecord.new("Parameter 'priority' should be an integer")
end
end
if !server["subprojects"].is_a?(Array) or server["subprojects"].empty?
raise InvalidRecord.new("Parameter 'subprojects' must be a not empty array")
end
if server["subprojects"].size > 1
check_provider(server["provider"])
# strings
%w{image flavor provider}.each do |p|
begin
check_string!(server[p])
rescue ArgumentError
raise InvalidRecord.new("Parameter '#{p}' must be a not empty string")
end
end
# arrays
%w{subnets groups}.each do |p|
begin
raise ArgumentError if !server[p].is_a?(Array) or server[p].empty?
server[p].each do |v|
raise ArgumentError unless v.is_a?(String)
end
rescue ArgumentError
raise InvalidRecord.new("Parameter '#{p}' must be a not empty array of strings")
end
end
p = ::Provider::ProviderFactory.get(server["provider"])
check_flavor!(p, server["flavor"])
check_image!(p, server["image"])
check_subnets_and_groups!(p, server["subnets"], server["groups"])
end
names = {}
server["subprojects"].each_with_index do |sp, spi|
begin
raise InvalidRecord.new("Parameter 'subprojects' must contains objects only") unless sp.is_a?(Hash)
%w{name env}.each do |p|
begin
check_string!(sp[p])
rescue ArgumentError
raise InvalidRecord.new("Parameter '#{p}' must be a not empty string")
end
end
rescue InvalidRecord => e
raise InvalidRecord.new("Subproject '#{spi}'. #{e.message}")
end
end
pdb = ::Devops::Db.connector.project_names_with_envs(server["subprojects"].map{|sp| sp["name"]})
server["subprojects"].each_with_index do |sp, spi|
raise InvalidRecord.new("Subproject '#{spi}'. Project '#{sp["name"]}' with env '#{sp["env"]}' does not exist") if pdb[sp["name"]].nil? or !pdb[sp["name"]].include?(sp["env"])
end
rescue InvalidRecord => e
raise InvalidRecord.new("Server '#{i}'. #{e.message}")
end end
def validate!
super
e = []
check_users!(self.users)
unless self.expires.nil?
check_expires!(self.expires)
end
self.servers.each_with_index do |server, i|
begin
if server["priority"].nil?
server["priority"] = 100
else
begin
Integer(server["priority"])
rescue ArgumentError, TypeError
raise InvalidRecord.new("Parameter 'priority' should be an integer")
end
end
if !server["subprojects"].is_a?(Array) or server["subprojects"].empty?
raise InvalidRecord.new("Parameter 'subprojects' must be a not empty array")
end
if server["subprojects"].size > 1
check_provider(server["provider"])
# strings
%w{image flavor provider}.each do |p|
begin
check_string!(server[p])
rescue ArgumentError
raise InvalidRecord.new("Parameter '#{p}' must be a not empty string")
end
end
# arrays
%w{subnets groups}.each do |p|
begin
raise ArgumentError if !server[p].is_a?(Array) or server[p].empty?
server[p].each do |v|
raise ArgumentError unless v.is_a?(String)
end
rescue ArgumentError
raise InvalidRecord.new("Parameter '#{p}' must be a not empty array of strings")
end
end
p = ::Provider::ProviderFactory.get(server["provider"])
check_flavor!(p, server["flavor"])
check_image!(p, server["image"])
check_subnets_and_groups!(p, server["subnets"], server["groups"])
end
names = {}
server["subprojects"].each_with_index do |sp, spi|
begin
raise InvalidRecord.new("Parameter 'subprojects' must contains objects only") unless sp.is_a?(Hash)
%w{name env}.each do |p|
begin
check_string!(sp[p])
rescue ArgumentError
raise InvalidRecord.new("Parameter '#{p}' must be a not empty string")
end
end
rescue InvalidRecord => e
raise InvalidRecord.new("Subproject '#{spi}'. #{e.message}")
end
end
pdb = ::Devops::Db.connector.project_names_with_envs(server["subprojects"].map{|sp| sp["name"]})
server["subprojects"].each_with_index do |sp, spi|
raise InvalidRecord.new("Subproject '#{spi}'. Project '#{sp["name"]}' with env '#{sp["env"]}' does not exist") if pdb[sp["name"]].nil? or !pdb[sp["name"]].include?(sp["env"])
end
rescue InvalidRecord => e
raise InvalidRecord.new("Server '#{i}'. #{e.message}")
end
end
true
rescue InvalidRecord => e
raise InvalidRecord.new "Deploy environment '#{self.identifier}'. " + e.message
end
def to_hash
{
"identifier" => self.identifier,
"expires" => self.expires,
"users" => self.users,
"servers" => self.servers
}
end
def self.build_from_bson d
DeployEnvMulti.new(d)
end
def self.create hash
DeployEnvMulti.new(hash)
end
end end
true
rescue InvalidRecord => e
raise InvalidRecord.new "Deploy environment '#{self.identifier}'. " + e.message
end end
def to_hash
{
"identifier" => self.identifier,
"expires" => self.expires,
"users" => self.users,
"servers" => self.servers
}
end
def self.build_from_bson d
DeployEnvMulti.new(d)
end
def self.create hash
DeployEnvMulti.new(hash)
end
end end

View File

@ -1,57 +1,61 @@
require "db/mongo/models/deploy_env/deploy_env_base" require "db/mongo/models/deploy_env/deploy_env_base"
require "providers/provider_factory" require "providers/provider_factory"
class DeployEnvOpenstack < DeployEnvBase module Devops
module Model
class DeployEnvOpenstack < DeployEnvBase
attr_accessor :flavor, :image, :subnets, :groups attr_accessor :flavor, :image, :subnets, :groups
types :identifier => {:type => String, :empty => false}, types :identifier => {:type => String, :empty => false},
:image => {:type => String, :empty => false}, :image => {:type => String, :empty => false},
:flavor => {:type => String, :empty => false}, :flavor => {:type => String, :empty => false},
:provider => {:type => String, :empty => false}, :provider => {:type => String, :empty => false},
:expires => {:type => String, :empty => false, :nil => true}, :expires => {:type => String, :empty => false, :nil => true},
:run_list => {:type => Array, :empty => true}, :run_list => {:type => Array, :empty => true},
:users => {:type => Array, :empty => true}, :users => {:type => Array, :empty => true},
:subnets => {:type => Array, :empty => true}, :subnets => {:type => Array, :empty => true},
:groups => {:type => Array, :empty => false} :groups => {:type => Array, :empty => false}
set_validators ::Validators::DeployEnv::RunList, set_validators ::Validators::DeployEnv::RunList,
::Validators::DeployEnv::Expiration, ::Validators::DeployEnv::Expiration,
::Validators::DeployEnv::Users, ::Validators::DeployEnv::Users,
::Validators::DeployEnv::Flavor, ::Validators::DeployEnv::Flavor,
::Validators::DeployEnv::Image, ::Validators::DeployEnv::Image,
::Validators::DeployEnv::SubnetNotEmpty, ::Validators::DeployEnv::SubnetNotEmpty,
::Validators::DeployEnv::SubnetBelongsToProvider, ::Validators::DeployEnv::SubnetBelongsToProvider,
::Validators::DeployEnv::Groups ::Validators::DeployEnv::Groups
def initialize d={} def initialize d={}
super(d) super(d)
self.flavor = d["flavor"] self.flavor = d["flavor"]
self.image = d["image"] self.image = d["image"]
b = d["subnets"] || [] b = d["subnets"] || []
self.subnets = (b.is_a?(Array) ? b.uniq : b) self.subnets = (b.is_a?(Array) ? b.uniq : b)
b = d["groups"] || ["default"] b = d["groups"] || ["default"]
self.groups = (b.is_a?(Array) ? b.uniq : b) self.groups = (b.is_a?(Array) ? b.uniq : b)
end
def to_hash
h = super
h.merge!({
"flavor" => self.flavor,
"image" => self.image,
"subnets" => self.subnets,
"groups" => self.groups
})
end
def self.create hash
DeployEnvOpenstack.new(hash)
end
private
def subnets_filter
nil
end
end
end end
def to_hash
h = super
h.merge!({
"flavor" => self.flavor,
"image" => self.image,
"subnets" => self.subnets,
"groups" => self.groups
})
end
def self.create hash
DeployEnvOpenstack.new(hash)
end
private
def subnets_filter
nil
end
end end

View File

@ -1,45 +1,31 @@
require "db/mongo/models/deploy_env/deploy_env_base" require "db/mongo/models/deploy_env/deploy_env_base"
class DeployEnvStatic < DeployEnvBase module Devops
module Model
class DeployEnvStatic < DeployEnvBase
types :identifier => {:type => String, :empty => false}, types :identifier => {:type => String, :empty => false},
:provider => {:type => String, :empty => false}, :provider => {:type => String, :empty => false},
:expires => {:type => String, :empty => false, :nil => true}, :expires => {:type => String, :empty => false, :nil => true},
:run_list => {:type => Array, :empty => true}, :run_list => {:type => Array, :empty => true},
:users => {:type => Array, :empty => true} :users => {:type => Array, :empty => true}
set_validators ::Validators::DeployEnv::RunList, set_validators ::Validators::DeployEnv::RunList,
::Validators::DeployEnv::Expiration, ::Validators::DeployEnv::Expiration,
::Validators::DeployEnv::Users ::Validators::DeployEnv::Users
def initialize d={} def initialize d={}
super(d) super(d)
=begin end
self.identifier = d["identifier"]
b = d["run_list"] || [] def to_hash
self.run_list = (b.is_a?(Array) ? b.uniq : b) super
self.expires = d["expires"] end
self.provider = d["provider"]
b = d["users"] || [] def self.create hash
self.users = (b.is_a?(Array) ? b.uniq : b) DeployEnvStatic.new(hash)
=end end
end
end end
def to_hash
super
=begin
{
"identifier" => self.identifier,
"run_list" => self.run_list,
"expires" => self.expires,
"provider" => self.provider,
"users" => self.users
}
=end
end
def self.create hash
DeployEnvStatic.new(hash)
end
end end

View File

@ -1,43 +1,47 @@
require "db/exceptions/invalid_record" require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model" require "db/mongo/models/mongo_model"
class Image < MongoModel module Devops
module Model
class Image < MongoModel
attr_accessor :id, :provider, :remote_user, :name, :bootstrap_template attr_accessor :id, :provider, :remote_user, :name, :bootstrap_template
types :id => {:type => String, :empty => false}, types :id => {:type => String, :empty => false},
:provider => {:type => String, :empty => false}, :provider => {:type => String, :empty => false},
:remote_user => {:type => String, :empty => false}, :remote_user => {:type => String, :empty => false},
:name => {:type => String, :empty => true}, :name => {:type => String, :empty => true},
:bootstrap_template => {:type => String, :empty => false, :nil => true} :bootstrap_template => {:type => String, :empty => false, :nil => true}
set_validators ::Validators::Image::ImageInFilter, set_validators ::Validators::Image::ImageInFilter,
::Validators::Image::BootstrapTemplate ::Validators::Image::BootstrapTemplate
def initialize p={} def initialize p={}
self.id = p["id"] self.id = p["id"]
self.provider = p["provider"] self.provider = p["provider"]
self.remote_user = p["remote_user"] self.remote_user = p["remote_user"]
self.name = p["name"] || "" self.name = p["name"] || ""
self.bootstrap_template = p["bootstrap_template"] self.bootstrap_template = p["bootstrap_template"]
end
def self.build_from_bson args
image = Image.new(args)
image.id = args["_id"]
image
end
def to_hash_without_id
o = {
"provider" => self.provider,
"name" => self.name,
"remote_user" => self.remote_user
}
o["bootstrap_template"] = self.bootstrap_template
o
end
def self.create_from_json! json
Image.new( JSON.parse(json) )
end
end
end end
def self.build_from_bson args
image = Image.new(args)
image.id = args["_id"]
image
end
def to_hash_without_id
o = {
"provider" => self.provider,
"name" => self.name,
"remote_user" => self.remote_user
}
o["bootstrap_template"] = self.bootstrap_template
o
end
def self.create_from_json! json
Image.new( JSON.parse(json) )
end
end end

View File

@ -2,45 +2,49 @@ require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model" require "db/mongo/models/mongo_model"
require "json" require "json"
class Key < MongoModel module Devops
module Model
class Key < MongoModel
SYSTEM = "system" SYSTEM = "system"
USER = "user" USER = "user"
attr_accessor :id, :path, :scope attr_accessor :id, :path, :scope
types :id => {:type => String, :empty => false}, types :id => {:type => String, :empty => false},
:path => {:type => String, :empty => false}, :path => {:type => String, :empty => false},
:scope => {:type => String, :empty => false} :scope => {:type => String, :empty => false}
set_validators ::Validators::Key::FileExistence, set_validators ::Validators::Key::FileExistence,
::Validators::Key::Scope ::Validators::Key::Scope
def initialize p={} def initialize p={}
self.id = p["id"] self.id = p["id"]
self.path = p["path"] self.path = p["path"]
self.scope = p["scope"] || USER self.scope = p["scope"] || USER
end
def self.build_from_bson s
key = Key.new s
key.id = s["_id"]
key
end
def self.create_from_json json
Key.new( JSON.parse(json) )
end
def filename
File.basename(self.path)
end
def to_hash_without_id
o = {
"path" => self.path,
"scope" => self.scope
}
o
end
end
end end
def self.build_from_bson s
key = Key.new s
key.id = s["_id"]
key
end
def self.create_from_json json
Key.new( JSON.parse(json) )
end
def filename
File.basename(self.path)
end
def to_hash_without_id
o = {
"path" => self.path,
"scope" => self.scope
}
o
end
end end

View File

@ -2,145 +2,149 @@ require "providers/provider_factory"
require "db/exceptions/invalid_record" require "db/exceptions/invalid_record"
require "json" require "json"
class MongoModel module Devops
module Model
class MongoModel
# multi_json sends argument to 'to_json' method # multi_json sends argument to 'to_json' method
def to_json arg=nil def to_json arg=nil
JSON.pretty_generate self.to_hash JSON.pretty_generate self.to_hash
end end
def to_hash def to_hash
h = to_hash_without_id h = to_hash_without_id
h["id"] = self.id h["id"] = self.id
h h
end end
def to_mongo_hash def to_mongo_hash
h = to_hash_without_id h = to_hash_without_id
h["_id"] = self.id h["_id"] = self.id
h h
end end
def is_empty? val def is_empty? val
val.nil? or val.strip.empty? val.nil? or val.strip.empty?
end end
def check_string! val def check_string! val
raise ArgumentError unless val.is_a?(String) raise ArgumentError unless val.is_a?(String)
val.strip! val.strip!
raise ArgumentError if val.empty? raise ArgumentError if val.empty?
end end
def check_array! val, type, empty=false def check_array! val, type, empty=false
raise ArgumentError unless val.is_a?(Array) raise ArgumentError unless val.is_a?(Array)
raise ArgumentError if !empty and val.empty? raise ArgumentError if !empty and val.empty?
val.each do |v| val.each do |v|
raise ArgumentError unless v.is_a?(type) raise ArgumentError unless v.is_a?(type)
end
end
def check_name_value val
raise ArgumentError.new "Invalid name, it should contains 0-9, a-z, A-Z, _, - symbols only" if val.match(/^[0-9a-zA-Z_\-]+$/).nil?
end
def check_provider provider=self.provider
unless ::Provider::ProviderFactory.providers.include?(provider)
raise InvalidRecord.new "Invalid provider '#{provider}'"
end
end
def validate!
begin
self.validate_fields_types
self.class.validate_model(self)
true
rescue InvalidRecord => e
error_message = self.build_error_message(e.message)
raise InvalidRecord.new(error_message)
end
end
def build_error_message(message)
# overrided in descendants
message
end
# types - Hash
# key - param name
# value - Hash
# :type - param type
# :empty - can param be empty? (false)
# :nil - can param be nil? (false)
# :value_type - type of array element (String)
def self.types types
define_method :validate_fields_types do
t = types.keys
e = types.keys
n = types.keys
types.each do |name, value|
if value[:nil]
n.delete(name)
if self.send(name).nil?
e.delete(name)
t.delete(name)
next
end
else
n.delete(name) unless self.send(name).nil?
end end
if self.send(name).is_a? value[:type] end
t.delete(name)
self.send(name).strip! if value[:type] == String def check_name_value val
if value[:type] == Array raise ArgumentError.new "Invalid name, it should contains 0-9, a-z, A-Z, _, - symbols only" if val.match(/^[0-9a-zA-Z_\-]+$/).nil?
unless value[:value_type] == false end
type = value[:value_type] || String
self.send(name).each do |e| def check_provider provider=self.provider
unless e.is_a?(type) unless ::Provider::ProviderFactory.providers.include?(provider)
t.push(name) raise InvalidRecord.new "Invalid provider '#{provider}'"
break end
end
def validate!
begin
self.validate_fields_types
self.class.validate_model(self)
true
rescue InvalidRecord => e
error_message = self.build_error_message(e.message)
raise InvalidRecord.new(error_message)
end
end
def build_error_message(message)
# overrided in descendants
message
end
# types - Hash
# key - param name
# value - Hash
# :type - param type
# :empty - can param be empty? (false)
# :nil - can param be nil? (false)
# :value_type - type of array element (String)
def self.types types
define_method :validate_fields_types do
t = types.keys
e = types.keys
n = types.keys
types.each do |name, value|
if value[:nil]
n.delete(name)
if self.send(name).nil?
e.delete(name)
t.delete(name)
next
end
else
n.delete(name) unless self.send(name).nil?
end
if self.send(name).is_a? value[:type]
t.delete(name)
self.send(name).strip! if value[:type] == String
if value[:type] == Array
unless value[:value_type] == false
type = value[:value_type] || String
self.send(name).each do |e|
unless e.is_a?(type)
t.push(name)
break
end
end
end end
end end
e.delete(name) if value[:empty] or !self.send(name).empty?
end end
end end
e.delete(name) if value[:empty] or !self.send(name).empty? raise InvalidRecord.new "Parameter(s) '#{n.join("', '")}' can not be undefined" unless n.empty?
raise InvalidRecord.new "Parameter(s) '#{t.join("', '")}' have invalid type(s)" unless t.empty?
raise InvalidRecord.new "Parameter(s) '#{e.join("', '")}' can not be empty" unless e.empty?
if types.has_key? :provider
self.send("check_provider")
end
true
end end
end end
raise InvalidRecord.new "Parameter(s) '#{n.join("', '")}' can not be undefined" unless n.empty?
raise InvalidRecord.new "Parameter(s) '#{t.join("', '")}' have invalid type(s)" unless t.empty? def self.validators
raise InvalidRecord.new "Parameter(s) '#{e.join("', '")}' can not be empty" unless e.empty? @validators || []
if types.has_key? :provider
self.send("check_provider")
end end
true
# all exceptions are handled in @validate! method
def self.validate_model(model)
validators.each do |validator|
validator.new(model).validate!
end
end
# private class methods
class << self
private
def set_validators(*validators_to_add)
@validators ||= []
@validators += validators_to_add
end
def unset_validators(*validators_to_remove)
@validators ||= []
self.validators -= validators_to_remove
end
end
end end
end end
def self.validators
@validators || []
end
# all exceptions are handled in @validate! method
def self.validate_model(model)
validators.each do |validator|
validator.new(model).validate!
end
end
# private class methods
class << self
private
def set_validators(*validators_to_add)
@validators ||= []
@validators += validators_to_add
end
def unset_validators(*validators_to_remove)
@validators ||= []
self.validators -= validators_to_remove
end
end
end end

View File

@ -6,134 +6,138 @@ require "db/mongo/models/deploy_env/deploy_env_multi"
require "db/mongo/models/mongo_model" require "db/mongo/models/mongo_model"
require "json" require "json"
class Project < MongoModel module Devops
module Model
class Project < MongoModel
attr_accessor :id, :deploy_envs, :type, :archived, :description attr_accessor :id, :deploy_envs, :type, :archived, :description
types :id => {:type => String, :empty => false}, types :id => {:type => String, :empty => false},
:deploy_envs => {:type => Array, :value_type => false, :empty => false}, :deploy_envs => {:type => Array, :value_type => false, :empty => false},
:description => {:type => String, :empty => true, :nil => true} :description => {:type => String, :empty => true, :nil => true}
MULTI_TYPE = "multi" MULTI_TYPE = "multi"
def self.fields def self.fields
["deploy_envs", "type", "description"] ["deploy_envs", "type", "description"]
end end
def initialize p={} def initialize p={}
self.id = p["name"] self.id = p["name"]
#raise InvalidRecord.new "No deploy envirenments for project #{self.id}" if p["deploy_envs"].nil? or p["deploy_envs"].empty? #raise InvalidRecord.new "No deploy envirenments for project #{self.id}" if p["deploy_envs"].nil? or p["deploy_envs"].empty?
self.type = p["type"] self.type = p["type"]
self.description = p["description"] self.description = p["description"]
self.archived = p["archived"] || false self.archived = p["archived"] || false
env_class = ( self.multi? ? DeployEnvMulti : DeployEnvFactory ) env_class = ( self.multi? ? DeployEnvMulti : DeployEnvFactory )
unless p["deploy_envs"].nil? unless p["deploy_envs"].nil?
self.deploy_envs = [] self.deploy_envs = []
p["deploy_envs"].each do |e| p["deploy_envs"].each do |e|
env = env_class.create(e) env = env_class.create(e)
self.deploy_envs.push env
end
end
end
def deploy_env env
de = self.deploy_envs.detect {|e| e.identifier == env}
raise RecordNotFound.new("Project '#{self.id}' does not have deploy environment '#{env}'") if de.nil?
de
end
def add_authorized_user user, env=nil
return if user.nil?
new_users = ( user.is_a?(Array) ? user : [ user ] )
if env.nil?
self.deploy_envs.each do |e|
return unless e.users.is_a?(Array)
e.users = (e.users + new_users).uniq
end
else
e = self.deploy_env(env)
return unless e.users.is_a?(Array)
e.users = (e.users + new_users).uniq
end
end
def remove_authorized_user user, env=nil
return if user.nil?
users = ( user.is_a?(Array) ? user : [ user ] )
if env.nil?
self.deploy_envs.each do |e|
return unless e.users.is_a?(Array)
e.users = e.users - users
end
else
e = self.deploy_env(env)
return unless e.users.is_a?(Array)
e.users = e.users - users
end
end
def check_authorization user_id, env
e = self.deploy_env(env)
return true if user_id == User::ROOT_USER_NAME
return e.users.include? user_id
rescue RecordNotFound => e
return false
end
def validate!
super
check_name_value(self.id)
envs = self.deploy_envs.map {|d| d.identifier}
non_uniq = envs.uniq.select{|u| envs.count(u) > 1}
raise InvalidRecord.new "Deploy environment(s) '#{non_uniq.join("', '")}' is/are not unique" unless non_uniq.empty?
self.deploy_envs.each do |d|
d.validate!
unless self.multi?
rn = "#{self.id}#{DevopsConfig.config[:role_separator] || "_"}#{d.identifier}"
role = "role[#{rn}]"
d.run_list = d.run_list - [rn, role]
d.run_list.unshift(role)
end
end
true
rescue InvalidRecord, ArgumentError => e
raise InvalidRecord.new "Project '#{self.id}'. #{e.message}"
end
def remove_env env
self.deploy_envs.delete_if {|e| e.identifier == env}
end
def add_env env
raise InvalidRecord.new "Deploy environment '#{env.identifier}' for project '#{self.id}' already exist" unless self.deploy_env(env.identifier).nil?
self.deploy_envs.push env self.deploy_envs.push env
end end
end
end
def deploy_env env def to_hash
de = self.deploy_envs.detect {|e| e.identifier == env} h = self.to_hash_without_id
raise RecordNotFound.new("Project '#{self.id}' does not have deploy environment '#{env}'") if de.nil? h["name"] = self.id
de h
end
def add_authorized_user user, env=nil
return if user.nil?
new_users = ( user.is_a?(Array) ? user : [ user ] )
if env.nil?
self.deploy_envs.each do |e|
return unless e.users.is_a?(Array)
e.users = (e.users + new_users).uniq
end end
else
e = self.deploy_env(env)
return unless e.users.is_a?(Array)
e.users = (e.users + new_users).uniq
end
end
def remove_authorized_user user, env=nil def to_hash_without_id
return if user.nil? h = {}
users = ( user.is_a?(Array) ? user : [ user ] ) h["deploy_envs"] = self.deploy_envs.map {|e| e.to_hash} unless self.deploy_envs.nil?
if env.nil? h["archived"] = self.archived
self.deploy_envs.each do |e| h["description"] = self.description
return unless e.users.is_a?(Array) if self.multi?
e.users = e.users - users h["type"] = MULTI_TYPE
end
h
end end
else
e = self.deploy_env(env)
return unless e.users.is_a?(Array)
e.users = e.users - users
end
end
def check_authorization user_id, env def multi?
e = self.deploy_env(env) self.type == MULTI_TYPE
return true if user_id == User::ROOT_USER_NAME
return e.users.include? user_id
rescue RecordNotFound => e
return false
end
def validate!
super
check_name_value(self.id)
envs = self.deploy_envs.map {|d| d.identifier}
non_uniq = envs.uniq.select{|u| envs.count(u) > 1}
raise InvalidRecord.new "Deploy environment(s) '#{non_uniq.join("', '")}' is/are not unique" unless non_uniq.empty?
self.deploy_envs.each do |d|
d.validate!
unless self.multi?
rn = "#{self.id}#{DevopsConfig.config[:role_separator] || "_"}#{d.identifier}"
role = "role[#{rn}]"
d.run_list = d.run_list - [rn, role]
d.run_list.unshift(role)
end end
def self.build_from_bson p
p["name"] = p["_id"]
Project.new p
end
end end
true
rescue InvalidRecord, ArgumentError => e
raise InvalidRecord.new "Project '#{self.id}'. #{e.message}"
end end
def remove_env env
self.deploy_envs.delete_if {|e| e.identifier == env}
end
def add_env env
raise InvalidRecord.new "Deploy environment '#{env.identifier}' for project '#{self.id}' already exist" unless self.deploy_env(env.identifier).nil?
self.deploy_envs.push env
end
def to_hash
h = self.to_hash_without_id
h["name"] = self.id
h
end
def to_hash_without_id
h = {}
h["deploy_envs"] = self.deploy_envs.map {|e| e.to_hash} unless self.deploy_envs.nil?
h["archived"] = self.archived
h["description"] = self.description
if self.multi?
h["type"] = MULTI_TYPE
end
h
end
def multi?
self.type == MULTI_TYPE
end
def self.build_from_bson p
p["name"] = p["_id"]
Project.new p
end
end end

View File

@ -1,34 +1,38 @@
require "db/exceptions/invalid_record" require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model" require "db/mongo/models/mongo_model"
class Report < MongoModel module Devops
module Model
class Report < MongoModel
DEPLOY_TYPE = 1 DEPLOY_TYPE = 1
SERVER_TYPE = 2 SERVER_TYPE = 2
BOOTSTRAP_TYPE = 3 BOOTSTRAP_TYPE = 3
PROJECT_TEST_TYPE = 4 PROJECT_TEST_TYPE = 4
attr_accessor :id, :file, :created_at, :created_by, :project, :deploy_env, :type attr_accessor :id, :file, :created_at, :created_by, :project, :deploy_env, :type
def initialize r def initialize r
self.id = r["_id"] self.id = r["_id"]
self.file = r["file"] self.file = r["file"]
self.created_by = r["created_by"] self.created_by = r["created_by"]
self.project = r["project"] self.project = r["project"]
self.deploy_env = r["deploy_env"] self.deploy_env = r["deploy_env"]
self.type = r["type"] self.type = r["type"]
self.created_at = r["created_at"] self.created_at = r["created_at"]
end
def to_hash_without_id
{
"file" => self.file,
"created_at" => self.created_at,
"created_by" => self.created_by,
"project" => self.project,
"deploy_env" => self.deploy_env,
"type" => self.type
}
end
end
end end
def to_hash_without_id
{
"file" => self.file,
"created_at" => self.created_at,
"created_by" => self.created_by,
"project" => self.project,
"deploy_env" => self.deploy_env,
"type" => self.type
}
end
end end

View File

@ -1,89 +1,93 @@
require "db/exceptions/invalid_record" require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model" require "db/mongo/models/mongo_model"
class Server < MongoModel module Devops
module Model
class Server < MongoModel
attr_accessor :provider, :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by, :reserved_by attr_accessor :provider, :chef_node_name, :id, :remote_user, :project, :deploy_env, :private_ip, :public_ip, :created_at, :without_bootstrap, :created_by, :reserved_by
attr_accessor :options, :static, :key attr_accessor :options, :static, :key
types :id => {:type => String, :empty => false}, types :id => {:type => String, :empty => false},
:provider => {:type => String, :empty => false}, :provider => {:type => String, :empty => false},
:remote_user => {:type => String, :empty => false}, :remote_user => {:type => String, :empty => false},
:project => {:type => String, :empty => false}, :project => {:type => String, :empty => false},
:deploy_env => {:type => String, :empty => false}, :deploy_env => {:type => String, :empty => false},
:private_ip => {:type => String, :empty => false}, :private_ip => {:type => String, :empty => false},
:public_ip => {:type => String, :empty => true, :nil => true}, :public_ip => {:type => String, :empty => true, :nil => true},
:key => {:type => String, :empty => false}, :key => {:type => String, :empty => false},
:created_by => {:type => String, :empty => false}, :created_by => {:type => String, :empty => false},
:chef_node_name => {:type => String, :empty => true}, :chef_node_name => {:type => String, :empty => true},
:reserved_by => {:type => String, :empty => true} :reserved_by => {:type => String, :empty => true}
def self.fields def self.fields
["chef_node_name", "project", "deploy_env", "provider", "remote_user", "private_ip", "public_ip", "created_at", "created_by", "static", "key", "reserved_by"] ["chef_node_name", "project", "deploy_env", "provider", "remote_user", "private_ip", "public_ip", "created_at", "created_by", "static", "key", "reserved_by"]
end
def initialize s={}
self.provider = s["provider"]
self.chef_node_name = s["chef_node_name"]
self.id = s["_id"]
self.remote_user = s["remote_user"]
self.project = s["project"]
self.deploy_env = s["deploy_env"]
self.public_ip = s["public_ip"]
self.private_ip = s["private_ip"]
self.created_at = s["created_at"]
self.created_by = s["created_by"]
self.static = s["static"]
self.key = s["key"]
self.reserved_by = s["reserved_by"]
end
def validate!
super
true
end
def to_hash_without_id
{
"provider" => self.provider,
"chef_node_name" => self.chef_node_name,
"remote_user" => self.remote_user,
"project" => self.project,
"deploy_env" => self.deploy_env,
"private_ip" => self.private_ip,
"public_ip" => self.public_ip,
"created_at" => self.created_at,
"created_by" => self.created_by,
"static" => self.static,
"key" => self.key,
"reserved_by" => self.reserved_by
}.delete_if{|k,v| v.nil?}
end
def to_list_hash
{
"id" => self.id,
"chef_node_name" => self.chef_node_name
}
end
def self.build_from_bson s
Server.new(s)
end
def info
str = "Instance Name: #{self.chef_node_name}\n"
str << "Instance ID: #{self.id}\n"
str << "Private IP: #{self.private_ip}\n"
str << "Public IP: #{self.public_ip}\n" unless self.public_ip.nil?
str << "Remote user: #{self.remote_user}\n"
str << "Project: #{self.project} - #{self.deploy_env}\n"
str << "Created by: #{self.created_by}"
str
end
def static?
self.static || false
end
end
end end
def initialize s={}
self.provider = s["provider"]
self.chef_node_name = s["chef_node_name"]
self.id = s["_id"]
self.remote_user = s["remote_user"]
self.project = s["project"]
self.deploy_env = s["deploy_env"]
self.public_ip = s["public_ip"]
self.private_ip = s["private_ip"]
self.created_at = s["created_at"]
self.created_by = s["created_by"]
self.static = s["static"]
self.key = s["key"]
self.reserved_by = s["reserved_by"]
end
def validate!
super
true
end
def to_hash_without_id
{
"provider" => self.provider,
"chef_node_name" => self.chef_node_name,
"remote_user" => self.remote_user,
"project" => self.project,
"deploy_env" => self.deploy_env,
"private_ip" => self.private_ip,
"public_ip" => self.public_ip,
"created_at" => self.created_at,
"created_by" => self.created_by,
"static" => self.static,
"key" => self.key,
"reserved_by" => self.reserved_by
}.delete_if{|k,v| v.nil?}
end
def to_list_hash
{
"id" => self.id,
"chef_node_name" => self.chef_node_name
}
end
def self.build_from_bson s
Server.new(s)
end
def info
str = "Instance Name: #{self.chef_node_name}\n"
str << "Instance ID: #{self.id}\n"
str << "Private IP: #{self.private_ip}\n"
str << "Public IP: #{self.public_ip}\n" unless self.public_ip.nil?
str << "Remote user: #{self.remote_user}\n"
str << "Project: #{self.project} - #{self.deploy_env}\n"
str << "Created by: #{self.created_by}"
str
end
def static?
self.static || false
end
end end

View File

@ -1,52 +1,56 @@
class StackBase < MongoModel module Devops
module Model
class StackBase < MongoModel
attr_accessor :id, :project, :deploy_env, :stack_template, :cloud_stack_id, :provider attr_accessor :id, :project, :deploy_env, :stack_template, :cloud_stack_id, :provider
types id: {type: String, empty: false}, types id: {type: String, empty: false},
provider: {type: String, empty: false}, provider: {type: String, empty: false},
project: {type: String, empty: false}, project: {type: String, empty: false},
deploy_env: {type: String, empty: false}, deploy_env: {type: String, empty: false},
stack_template: {type: String, empty: false}, stack_template: {type: String, empty: false},
cloud_stack_id: {type: String, empty: false} cloud_stack_id: {type: String, empty: false}
def initialize attrs={} def initialize attrs={}
self.id = attrs['id'] self.id = attrs['id']
self.provider = attrs['provider'] self.provider = attrs['provider']
self.project = attrs['project'] self.project = attrs['project']
self.deploy_env = attrs['deploy_env'] self.deploy_env = attrs['deploy_env']
self.stack_template = attrs['stack_template'] self.stack_template = attrs['stack_template']
self.cloud_stack_id = attrs['cloud_stack_id'] self.cloud_stack_id = attrs['cloud_stack_id']
self self
end
def to_hash_without_id
{
provider: provider,
project: self.project,
deploy_env: self.deploy_env,
stack_template: self.stack_template,
cloud_stack_id: self.cloud_stack_id
}
end
# attrs should include:
# - id (String)
# - provider (String)
# - deploy_env (String)
# - stack_template (String)
def self.create(attrs)
model = new(attrs)
model.create_stack_in_cloud!
model
end
def self.build_from_bson(attrs)
attrs['id'] = attrs["_id"]
self.new(attrs)
end
def create_stack_in_cloud!
raise 'override me'
end
end
end end
def to_hash_without_id
{
provider: provider,
project: self.project,
deploy_env: self.deploy_env,
stack_template: self.stack_template,
cloud_stack_id: self.cloud_stack_id
}
end
# attrs should include:
# - id (String)
# - provider (String)
# - deploy_env (String)
# - stack_template (String)
def self.create(attrs)
model = new(attrs)
model.create_stack_in_cloud!
model
end
def self.build_from_bson(attrs)
attrs['id'] = attrs["_id"]
self.new(attrs)
end
def create_stack_in_cloud!
raise 'override me'
end
end end

View File

@ -1,8 +1,12 @@
class StackEc2 < StackBase module Devops
module Model
class StackEc2 < StackBase
def create_stack_in_cloud! def create_stack_in_cloud!
# create stack in AWS # create stack in AWS
self.cloud_stack_id = 'arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83' self.cloud_stack_id = 'arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83'
end
end
end end
end end

View File

@ -2,25 +2,29 @@ require_relative "stack_base"
require_relative "stack_openstack" require_relative "stack_openstack"
require_relative "stack_ec2" require_relative "stack_ec2"
class StackFactory module Devops
module Model
class StackFactory
def self.create(provider, attrs) def self.create(provider, attrs)
get_class(provider).create(attrs) get_class(provider).create(attrs)
end end
def self.build_from_bson(provider, attrs) def self.build_from_bson(provider, attrs)
get_class(provider).build_from_bson(attrs) get_class(provider).build_from_bson(attrs)
end end
def self.get_class(provider)
case provider
when ::Provider::Openstack::PROVIDER
StackOpenstack
when ::Provider::Ec2::PROVIDER
StackEc2
else
raise InvalidRecord.new "Invalid provider: '#{provider}'"
end
end
def self.get_class(provider)
case provider
when ::Provider::Openstack::PROVIDER
StackOpenstack
when ::Provider::Ec2::PROVIDER
StackEc2
else
raise InvalidRecord.new "Invalid provider: '#{provider}'"
end end
end end
end end

View File

@ -1,10 +1,14 @@
class StackOpenstack < StackBase module Devops
module Model
class StackOpenstack < StackBase
def create_stack_in_cloud! def create_stack_in_cloud!
provider = ::Provider::ProviderFactory.get('openstack') provider = ::Provider::ProviderFactory.get('openstack')
provider.create_stack(self) provider.create_stack(self)
# # create stack in Openstack # # create stack in Openstack
# self.cloud_stack_id = '4c712026-dcd5-4664-90b8-0915494c1332' # self.cloud_stack_id = '4c712026-dcd5-4664-90b8-0915494c1332'
end
end
end end
end end

View File

@ -1,73 +1,77 @@
require 'tempfile' require 'tempfile'
require 'securerandom' require 'securerandom'
class StackTemplateBase < MongoModel module Devops
module Model
class StackTemplateBase < MongoModel
attr_accessor :id, :template_url, :template_json, :provider attr_accessor :id, :template_url, :template_json, :provider
# Few words about template_url: # Few words about template_url:
# In Amazon Cloudformation the template file must be stored on an Amazon S3 bucket, # In Amazon Cloudformation the template file must be stored on an Amazon S3 bucket,
# but for Openstack stacks it isn't neccessary (your may use local file). # but for Openstack stacks it isn't neccessary (your may use local file).
# I decided to enforce template_url strategy using in openstack to reach more common # I decided to enforce template_url strategy using in openstack to reach more common
# interface between different providers' stack templates. # interface between different providers' stack templates.
types id: {type: String, empty: false}, types id: {type: String, empty: false},
provider: {type: String, empty: false}, provider: {type: String, empty: false},
template_json: {type: String, empty: false}, template_json: {type: String, empty: false},
template_url: {type: String, empty: false} template_url: {type: String, empty: false}
def initialize(attrs) def initialize(attrs)
self.id = attrs['id'] self.id = attrs['id']
self.template_json = attrs['template_json'].to_s self.template_json = attrs['template_json'].to_s
self.template_url = attrs['template_url'] self.template_url = attrs['template_url']
self.provider = attrs['provider'] self.provider = attrs['provider']
self self
end end
def to_hash_without_id def to_hash_without_id
{ {
provider: provider, provider: provider,
template_json: template_json, template_json: template_json,
template_url: template_url template_url: template_url
} }
end end
# do not forget to destroy template files on template destroying # do not forget to destroy template files on template destroying
def delete_template_file_from_storage def delete_template_file_from_storage
raise 'Override me' raise 'Override me'
end end
# attrs should include: # attrs should include:
# - id (String) # - id (String)
# - provider (String) # - provider (String)
# - template_json (String) # - template_json (String)
def self.create(attrs) def self.create(attrs)
json = attrs['template_json'] json = attrs['template_json']
attrs['template_url'] = generate_template_file_and_upload_to_storage(attrs['id'], json) attrs['template_url'] = generate_template_file_and_upload_to_storage(attrs['id'], json)
new(attrs) new(attrs)
end end
def self.build_from_bson(attrs) def self.build_from_bson(attrs)
attrs['id'] = attrs["_id"] attrs['id'] = attrs["_id"]
self.new(attrs) self.new(attrs)
end end
class << self class << self
private private
def generate_template_file_and_upload_to_storage(id, json) def generate_template_file_and_upload_to_storage(id, json)
tempfile = Tempfile.new('foo') tempfile = Tempfile.new('foo')
tempfile.write(json) tempfile.write(json)
secure_filename = "#{id}-#{SecureRandom.hex}.template" secure_filename = "#{id}-#{SecureRandom.hex}.template"
upload_file_to_storage(secure_filename, tempfile.path) upload_file_to_storage(secure_filename, tempfile.path)
ensure ensure
tempfile.close tempfile.close
tempfile.unlink tempfile.unlink
end
def upload_file_to_storage(filename, file_path)
raise 'Override me'
end
end
end end
def upload_file_to_storage(filename, file_path)
raise 'Override me'
end
end end
end end

View File

@ -1,15 +1,19 @@
class StackTemplateEc2 < StackTemplateBase module Devops
module Model
class StackTemplateEc2 < StackTemplateBase
def delete_template_file_from_storage def delete_template_file_from_storage
raise 'Implement me' raise 'Implement me'
end end
class << self class << self
private private
def upload_file_to_storage(filename, path)
"https://s3.amazonaws.com/#{filename}"
end
end
def upload_file_to_storage(filename, path)
"https://s3.amazonaws.com/#{filename}"
end end
end end
end end

View File

@ -2,25 +2,29 @@ require_relative "stack_template_base"
require_relative "stack_template_openstack" require_relative "stack_template_openstack"
require_relative "stack_template_ec2" require_relative "stack_template_ec2"
class StackTemplateFactory module Devops
module Model
class StackTemplateFactory
def self.create(provider, attrs) def self.create(provider, attrs)
get_class(provider).create(attrs) get_class(provider).create(attrs)
end end
def self.build_from_bson(provider, attrs) def self.build_from_bson(provider, attrs)
get_class(provider).build_from_bson(attrs) get_class(provider).build_from_bson(attrs)
end end
def self.get_class(provider)
case provider
when ::Provider::Openstack::PROVIDER
StackTemplateOpenstack
when ::Provider::Ec2::PROVIDER
StackTemplateEc2
else
raise InvalidRecord.new "Invalid provider: '#{provider}'"
end
end
def self.get_class(provider)
case provider
when ::Provider::Openstack::PROVIDER
StackTemplateOpenstack
when ::Provider::Ec2::PROVIDER
StackTemplateEc2
else
raise InvalidRecord.new "Invalid provider: '#{provider}'"
end end
end end
end end

View File

@ -1,15 +1,19 @@
class StackTemplateOpenstack < StackTemplateBase module Devops
module Model
class StackTemplateOpenstack < StackTemplateBase
def delete_template_file_from_storage def delete_template_file_from_storage
raise 'Implement me' raise 'Implement me'
end end
class << self class << self
private private
def upload_file_to_storage(filename, path)
"https://openstack_host/v1/my_account/#{filename}"
end
end
def upload_file_to_storage(filename, path)
"https://openstack_host/v1/my_account/#{filename}"
end end
end end
end end

View File

@ -2,111 +2,115 @@ require "db/exceptions/invalid_record"
require "exceptions/invalid_command" require "exceptions/invalid_command"
require "db/mongo/models/mongo_model" require "db/mongo/models/mongo_model"
class User < MongoModel module Devops
module Model
class User < MongoModel
ROOT_USER_NAME = 'root' ROOT_USER_NAME = 'root'
ROOT_PASSWORD = '' ROOT_PASSWORD = ''
PRIVILEGES = ["r", "w", "x"] PRIVILEGES = ["r", "w", "x"]
PRIVILEGES_REGEX = /^r?w?x?$/ PRIVILEGES_REGEX = /^r?w?x?$/
attr_accessor :id, :password, :privileges, :email attr_accessor :id, :password, :privileges, :email
types :id => {:type => String, :empty => false}, types :id => {:type => String, :empty => false},
:email => {:type => String, :empty => false}, :email => {:type => String, :empty => false},
:password => {:type => String, :empty => true} :password => {:type => String, :empty => true}
def initialize p={} def initialize p={}
self.id = p['username'] self.id = p['username']
self.email = p['email'] self.email = p['email']
self.password = p['password'] self.password = p['password']
self.privileges = p["privileges"] || self.default_privileges self.privileges = p["privileges"] || self.default_privileges
end
def all_privileges
privileges_with_value("rwx")
end
def default_privileges
privileges_with_value("r", "user" => "")
end
def grant cmd, priv=''
if !priv.empty? and PRIVILEGES_REGEX.match(priv).to_s.empty?
raise InvalidCommand.new "Invalid privileges '#{priv}'. Available values are '#{PRIVILEGES.join("', '")}'"
end
raise InvalidPrivileges.new "Can't grant privileges to root" if self.id == ROOT_USER_NAME
case cmd
when "all"
self.privileges.each_key do |key|
self.privileges[key] = priv
end end
when ""
self.privileges = self.default_privileges def all_privileges
else privileges_with_value("rwx")
raise InvalidCommand.new "Unsupported command #{cmd}" unless self.all_privileges.include?(cmd) end
self.privileges[cmd] = priv
def default_privileges
privileges_with_value("r", "user" => "")
end
def grant cmd, priv=''
if !priv.empty? and PRIVILEGES_REGEX.match(priv).to_s.empty?
raise InvalidCommand.new "Invalid privileges '#{priv}'. Available values are '#{PRIVILEGES.join("', '")}'"
end
raise InvalidPrivileges.new "Can't grant privileges to root" if self.id == ROOT_USER_NAME
case cmd
when "all"
self.privileges.each_key do |key|
self.privileges[key] = priv
end
when ""
self.privileges = self.default_privileges
else
raise InvalidCommand.new "Unsupported command #{cmd}" unless self.all_privileges.include?(cmd)
self.privileges[cmd] = priv
end
end
def self.build_from_bson s
user = User.new s
user.id = s["_id"]
user
end
def self.create_from_json json
User.new( JSON.parse(json) )
end
def to_hash_without_id
o = {
"email" => self.email,
"password" => self.password,
"privileges" => self.privileges
}
o
end
def can?(command, privilege)
p = self.privileges[command] || []
p.include?(privilege)
end
def check_privilege cmd, priv
p = self.privileges[cmd]
return false if p.nil?
return p.include?(priv)
end
def self.create_root
root = User.new({'username' => ROOT_USER_NAME, 'password' => ROOT_PASSWORD})
root.privileges = root.all_privileges
root.email = "#{ROOT_USER_NAME}@host"
root
end
private
def privileges_with_value value, options={}
privileges = {}
[
'flavor',
'group',
'image',
'project',
'server',
'key',
'user',
'filter',
'network',
'provider',
'script',
'templates',
'stack_template',
'stack'
].each { |t| privileges.store(t, value) }
privileges.merge(options)
end
end end
end end
def self.build_from_bson s
user = User.new s
user.id = s["_id"]
user
end
def self.create_from_json json
User.new( JSON.parse(json) )
end
def to_hash_without_id
o = {
"email" => self.email,
"password" => self.password,
"privileges" => self.privileges
}
o
end
def can?(command, privilege)
p = self.privileges[command] || []
p.include?(privilege)
end
def check_privilege cmd, priv
p = self.privileges[cmd]
return false if p.nil?
return p.include?(priv)
end
def self.create_root
root = User.new({'username' => ROOT_USER_NAME, 'password' => ROOT_PASSWORD})
root.privileges = root.all_privileges
root.email = "#{ROOT_USER_NAME}@host"
root
end
private
def privileges_with_value value, options={}
privileges = {}
[
'flavor',
'group',
'image',
'project',
'server',
'key',
'user',
'filter',
'network',
'provider',
'script',
'templates',
'stack_template',
'stack'
].each { |t| privileges.store(t, value) }
privileges.merge(options)
end
end end

View File

@ -37,6 +37,10 @@ class DevopsConfig
@@config @@config
end end
def [](key)
@@config[key]
end
def first_private_ipv4 def first_private_ipv4
Socket.ip_address_list.detect{|intf| intf.ipv4_private?} Socket.ip_address_list.detect{|intf| intf.ipv4_private?}
end end

View File

@ -102,6 +102,8 @@ module Devops
helpers Sinatra::Streaming helpers Sinatra::Streaming
helpers Devops::Version2_0::Helpers helpers Devops::Version2_0::Helpers
register Sinatra::DevopsAuth
=begin =begin
use Rack::Auth::Basic do |username, password| use Rack::Auth::Basic do |username, password|
begin begin

View File

@ -45,7 +45,7 @@ module Devops
lambda { lambda {
check_privileges("image", "w") check_privileges("image", "w")
settings.mongo.image params[:image_id] settings.mongo.image params[:image_id]
image = ::Image.new(create_object_from_json_body) image = Devops::Model::Image.new(create_object_from_json_body)
image.id = params[:image_id] image.id = params[:image_id]
settings.mongo.image_update image settings.mongo.image_update image
create_response("Image '#{params[:image_id]}' has been updated") create_response("Image '#{params[:image_id]}' has been updated")

View File

@ -18,7 +18,7 @@ module Devops
check_privileges("project", "r") check_privileges("project", "r")
fields = [] fields = []
if params.key?("fields") and params["fields"].is_a?(Array) if params.key?("fields") and params["fields"].is_a?(Array)
::Project.fields.each do |k| Devops::Model::Project.fields.each do |k|
fields.push k if params["fields"].include?(k) fields.push k if params["fields"].include?(k)
end end
end end
@ -49,7 +49,7 @@ module Devops
body = create_object_from_json_body body = create_object_from_json_body
check_string(body["name"], "Parameter 'name' must be a not empty string") check_string(body["name"], "Parameter 'name' must be a not empty string")
check_array(body["deploy_envs"], "Parameter 'deploy_envs' must be a not empty array of objects", Hash) check_array(body["deploy_envs"], "Parameter 'deploy_envs' must be a not empty array of objects", Hash)
p = ::Project.new(body) p = Devops::Model::Project.new(body)
halt_response("Project '#{p.id}' already exist") if settings.mongo.is_project_exists?(p) halt_response("Project '#{p.id}' already exist") if settings.mongo.is_project_exists?(p)
p.add_authorized_user [request.env['REMOTE_USER']] p.add_authorized_user [request.env['REMOTE_USER']]
settings.mongo.project_insert p settings.mongo.project_insert p
@ -70,11 +70,11 @@ module Devops
def self.update_project def self.update_project
lambda { lambda {
check_privileges("project", "w") check_privileges("project", "w")
project = ::Project.new(create_object_from_json_body) project = Devops::Model::Project.new(create_object_from_json_body)
project.id = params[:project] project.id = params[:project]
old_project = settings.mongo.project params[:project] old_project = settings.mongo.project params[:project]
settings.mongo.project_update project settings.mongo.project_update project
roles = ::Project.create_new_roles(old_project, project, logger) roles = Devops::Model::Project.create_new_roles(old_project, project, logger)
info = "Project '#{project.id}' has been updated." + Project.create_roles_response(roles) info = "Project '#{project.id}' has been updated." + Project.create_roles_response(roles)
create_response(info) create_response(info)
} }
@ -181,7 +181,7 @@ module Devops
end end
end end
else else
dir = DevopsService.config[:report_dir_v2] dir = DevopsConfig[:report_dir_v2]
files = [] files = []
uri = URI.parse(request.url) uri = URI.parse(request.url)
servers.each do |s| servers.each do |s|
@ -190,9 +190,9 @@ module Devops
rescue InvalidPrivileges, RecordNotFound => e rescue InvalidPrivileges, RecordNotFound => e
next next
end end
jid = DeployWorker.perform_async(dir, s.to_hash, [], DevopsService.config) jid = DeployWorker.perform_async(dir, s.to_hash, [], DevopsConfig.config)
logger.info "Job '#{jid}' has been started" logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsService.config[:url_prefix]}/v2.0/report/" + jid uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s files.push uri.to_s
end end
json files json files
@ -234,23 +234,23 @@ module Devops
project = settings.mongo.project(params[:id]) project = settings.mongo.project(params[:id])
env = project.deploy_env params[:env] env = project.deploy_env params[:env]
logger.info "Test project '#{project.id}' and environment '#{env.identifier}'" logger.info "Test project '#{project.id}' and environment '#{env.identifier}'"
if env.provider == Provider::Static::PROVIDER if env.provider == ::Provider::Static::PROVIDER
msg = "Can not test environment with provider '#{Provider::Static::PROVIDER}'" msg = "Can not test environment with provider '#{::Provider::Static::PROVIDER}'"
Logger.warn msg Logger.warn msg
return [400, msg] return [400, msg]
end end
dir = DevopsService.config[:report_dir_v2] dir = DevopsConfig[:report_dir_v2]
uri = URI.parse(request.url) uri = URI.parse(request.url)
p = { p = {
:project => project.id, :project => project.id,
:env => env.identifier, :env => env.identifier,
:user => request.env['REMOTE_USER'] :user => request.env['REMOTE_USER']
} }
jid = ProjectTestWorker.perform_async(dir, p, DevopsService.config) jid = ProjectTestWorker.perform_async(dir, p, DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE Worker.set_status jid, Worker::STATUS::IN_QUEUE
logger.info "Job '#{jid}' has been created" logger.info "Job '#{jid}' has been created"
uri.path = "#{DevopsService.config[:url_prefix]}/v2.0/report/" + jid uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files = [uri.to_s] files = [uri.to_s]
sleep 1 sleep 1
json files json files
@ -283,7 +283,7 @@ module Devops
old_project.deploy_envs.each do |e| old_project.deploy_envs.each do |e|
new_project.remove_env(e.identifier) new_project.remove_env(e.identifier)
end end
::Project.create_roles new_project.id, new_project.deploy_envs, logger Devops::Model::Project.create_roles new_project.id, new_project.deploy_envs, logger
end end
def self.create_roles_response roles def self.create_roles_response roles

View File

@ -29,7 +29,7 @@ module Devops
check_privileges("server", "r") check_privileges("server", "r")
fields = [] fields = []
if params.key?("fields") and params["fields"].is_a?(Array) if params.key?("fields") and params["fields"].is_a?(Array)
Server.fields.each do |k| Devops::Model::Server.fields.each do |k|
fields.push k if params["fields"].include?(k) fields.push k if params["fields"].include?(k)
end end
end end
@ -113,16 +113,16 @@ module Devops
end end
end end
else else
dir = DevopsService.config[:report_dir_v2] dir = DevopsConfig[:report_dir_v2]
files = [] files = []
uri = URI.parse(request.url) uri = URI.parse(request.url)
servers.each do |s| servers.each do |s|
h = s.to_hash h = s.to_hash
h["options"] = s.options h["options"] = s.options
jid = CreateServerWorker.perform_async(dir, env.provider, h, request.env['REMOTE_USER'], DevopsService.config) jid = CreateServerWorker.perform_async(dir, env.provider, h, request.env['REMOTE_USER'], DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE Worker.set_status jid, Worker::STATUS::IN_QUEUE
logger.info "Job '#{jid}' has been started" logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsService.config[:url_prefix]}/v2.0/report/" + jid uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s files.push uri.to_s
end end
sleep 1 sleep 1
@ -254,16 +254,16 @@ module Devops
end end
end end
else else
dir = DevopsService.config[:report_dir_v2] dir = DevopsConfig[:report_dir_v2]
files = [] files = []
uri = URI.parse(request.url) uri = URI.parse(request.url)
h = s.to_hash h = s.to_hash
h["options"] = s.options h["options"] = s.options
h["_id"] = s.id h["_id"] = s.id
jid = BootstrapWorker.perform_async(dir, d.provider, h, request.env['REMOTE_USER'], DevopsService.config) jid = BootstrapWorker.perform_async(dir, d.provider, h, request.env['REMOTE_USER'], DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE Worker.set_status jid, Worker::STATUS::IN_QUEUE
logger.info "Job '#{jid}' has been started" logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsService.config[:url_prefix]}/v2.0/report/" + jid uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
uri.query = nil uri.query = nil
uri.fragment = nil uri.fragment = nil
files.push uri.to_s files.push uri.to_s
@ -289,7 +289,7 @@ module Devops
cert = settings.mongo.key(key) cert = settings.mongo.key(key)
provider = ::Provider::ProviderFactory.get("static") provider = ::Provider::ProviderFactory.get("static")
s = Server.new s = Devops::Model::Server.new
s.provider = provider.name s.provider = provider.name
s.project = project s.project = project
s.deploy_env = deploy_env s.deploy_env = deploy_env

View File

@ -22,7 +22,7 @@ module Devops
["username", "password", "email"].each do |p| ["username", "password", "email"].each do |p|
check_string(user[p], "Parameter '#{p}' must be a not empty string") check_string(user[p], "Parameter '#{p}' must be a not empty string")
end end
Devops::Db.connector.user_insert ::User.new(user) Devops::Db.connector.user_insert Devops::Model::User.new(user)
create_response("Created", nil, 201) create_response("Created", nil, 201)
} }
end end
@ -66,7 +66,7 @@ module Devops
check_privileges("user", "w") check_privileges("user", "w")
action = File.basename(request.path) action = File.basename(request.path)
u = File.basename(File.dirname(request.path)) u = File.basename(File.dirname(request.path))
raise InvalidPrivileges.new("Access denied for '#{request.env['REMOTE_USER']}'") if u == ::User::ROOT_USER_NAME and request.env['REMOTE_USER'] != ::User::ROOT_USER_NAME raise InvalidPrivileges.new("Access denied for '#{request.env['REMOTE_USER']}'") if u == Devops::Model::User::ROOT_USER_NAME and request.env['REMOTE_USER'] != Devops::Model::User::ROOT_USER_NAME
check_privileges("user", "w") unless request.env['REMOTE_USER'] == u check_privileges("user", "w") unless request.env['REMOTE_USER'] == u

View File

@ -2,12 +2,6 @@ require "sinatra/base"
module Sinatra module Sinatra
module HeadersHelpers
end
helpers HeadersHelpers
class Base class Base
class << self class << self
@ -16,6 +10,7 @@ module Sinatra
headers = opt.delete(:headers) || [] headers = opt.delete(:headers) || []
before path do before path do
check_headers *headers check_headers *headers
protect!
end end
get path, opt, &block get path, opt, &block
@ -25,6 +20,7 @@ module Sinatra
headers = opt.delete(:headers) || [] headers = opt.delete(:headers) || []
before path do before path do
check_headers *headers check_headers *headers
protect!
end end
post_with_statistic path, opt, &block post_with_statistic path, opt, &block
end end
@ -41,6 +37,7 @@ module Sinatra
headers = opt.delete(:headers) || [] headers = opt.delete(:headers) || []
before path do before path do
check_headers *headers check_headers *headers
protect!
end end
put path, opt, &block put path, opt, &block
@ -54,6 +51,7 @@ module Sinatra
headers = opt.delete(:headers) || [] headers = opt.delete(:headers) || []
before path do before path do
check_headers *headers check_headers *headers
protect!
end end
delete path, opt, &block delete path, opt, &block
@ -71,6 +69,7 @@ module Sinatra
else else
check_headers *headers check_headers *headers
end end
protect!
end end
hash.each do |method, block| hash.each do |method, block|