Merge branch 'devops_3' into devops_3_achuchkalov

This commit is contained in:
Anton Chuchkalov 2015-07-27 16:01:26 +02:00
commit beca955f60
113 changed files with 2341 additions and 2199 deletions

View File

@ -0,0 +1,14 @@
require "commands/bootstrap_templates"
module Devops
module API2_0
module Handler
class BootstrapTemplates
include BootstrapTemplatesCommands
end
end
end
end

View File

@ -0,0 +1,61 @@
require "commands/deploy"
require "commands/status"
require "workers/deploy_worker"
module Devops
module API2_0
module Handler
class Deploy
extend DeployCommands
extend StatusCommands
def initialize req, params
@request = req
@params = params
end
def deploy names, tags
dir = DevopsConfig.config[:report_dir_v2]
files = []
uri = URI.parse(@request.url)
servers(names).each do |s|
project = begin
Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
next
end
jid = DeployWorker.perform_async(dir, s.to_hash, tags, @request.env['REMOTE_USER'], DevopsConfig.config)
files.push jid
end
files
end
def deploy_stream out, names, tags
status = []
servers(names).each do |s|
project = begin
Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
out << e.message + "\n"
status.push 2
next
end
res = deploy_server_proc.call(out, s, tags)
status.push(res)
end
out << create_status(status)
rescue RecordNotFound => e
out << e.message
end
def servers names
servers = Devops::Db.connector.servers(nil, nil, names, true)
raise RecordNotFound.new("No reserved servers found for names '#{names.join("', '")}'") if servers.empty?
servers.sort_by!{|s| names.index(s.chef_node_name)}
servers
end
end
end
end
end

View File

@ -0,0 +1,24 @@
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Filter < RequestHandler
def available_images provider
Devops::Db.connector.available_images(provider)
end
def add_images images, provider
Devops::Db.connector.add_available_images(images, provider)
end
def delete_images images, provider
Devops::Db.connector.delete_available_images(images, provider)
end
end
end
end
end

View File

@ -0,0 +1,22 @@
require_relative "request_handler"
require "providers/provider_factory"
module Devops
module API2_0
module Handler
class Flavor < RequestHandler
def initialize request, params
@provider = params[:provider]
end
def flavors
p = ::Provider::ProviderFactory.get @provider
p.flavors
end
end
end
end
end

View File

@ -0,0 +1,20 @@
require "providers/provider_factory"
module Devops
module API2_0
module Handler
class Group
def initialize provider
@provider = provider
end
def groups params
p = ::Provider::ProviderFactory.get @provider
p.groups(params)
end
end
end
end
end

View File

@ -0,0 +1,51 @@
require "providers/provider_factory"
require "commands/image"
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Image < RequestHandler
extend ImageCommands
def images provider
Devops::Db.connector.images(provider)
end
def provider_images provider
Image.get_available_provider_images(Devops::Db.connector, provider)
end
def get_image id
Devops::Db.connector.image(id)
end
def create_image image
Devops::Db.connector.image_insert Devops::Model::Image.new(image)
end
def update_image id, image
Devops::Db.connector.image id
obj = Devops::Model::Image.new(image)
obj.id = id
Devops::Db.connector.image_update image
end
def delete_image id
projects = Devops::Db.connector.projects_by_image id
unless projects.empty?
ar = []
projects.each do |p|
ar += p.deploy_envs.select{|e| e.respond_to?(:image)}.select{|e| e.image == id}.map{|e| "#{p.id}.#{e.identifier}"}
end
raise DependencyError.new "Deleting is forbidden: Image is used in #{ar.join(", ")}"
end
Devops::Db.connector.image_delete id
end
end
end
end
end

View File

@ -0,0 +1,45 @@
require "db/mongo/models/key"
require "fileutils"
module Devops
module API2_0
module Handler
class Key
def keys
keys = Devops::DB.connector.keys.map {|i| i.to_hash}
keys.each {|k| k.delete("path")} # We should not return path to the key
end
def create body, file_name
File.open(file_name, "w") do |f|
f.write(body["content"])
f.chmod(0400)
end
key = Devops::Model::Key.new({"path" => file_name, "id" => body["key_name"]})
Devops::DB.connector.key_insert key
end
def delete key_id
mongo = Devops::DB.connector
servers = mongo.servers_by_key key_id
unless servers.empty?
s_str = servers.map{|s| s.id}.join(", ")
raise DependencyError.new "Deleting is forbidden: Key is used in servers: #{s_str}"
end
k = mongo.key key_id
begin
FileUtils.rm(k.path)
rescue
logger.error "Missing key file for #{key_id} - #{k.filename}"
end
mongo.key_delete key_id
end
end
end
end
end

View File

@ -0,0 +1,17 @@
require "providers/provider_factory"
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Network < RequestHandler
def networks provider
p = ::Provider::ProviderFactory.get provider
p.networks_detail
end
end
end
end
end

View File

@ -0,0 +1,231 @@
require "commands/deploy"
require "commands/status"
require "commands/server"
require "db/mongo/models/project"
require "workers/project_test_worker"
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Project < RequestHandler
extend DeployCommands
extend StatusCommands
extend ServerCommands
def projects
fields = []
if @params.key?("fields") and @params["fields"].is_a?(Array)
Devops::Model::Project.fields.each do |k|
fields.push k if @params["fields"].include?(k)
end
end
archived = @params.include?("archived")
Devops::Db.connector.projects(nil, nil, fields, archived)
end
def project id
Devops::Db.connector.project(id)
end
def project_servers id
Devops::Db.connector.project(id)
Devops::Db.connector.servers(id, @params[:deploy_env])
end
# TODO: multi project
def create_project body
p = Devops::Model::Project.new(body)
halt_response("Project '#{p.id}' already exist") if Devops::Db.connector.is_project_exists?(p)
p.add_authorized_user [@request.env['REMOTE_USER']]
p.create
if p.multi?
"Project '#{p.id}' with type 'multi' created"
else
roles = create_roles p.id, p.deploy_envs, logger
"Project '#{p.id}' created. " + create_roles_response(roles)
end
end
# TODO: multi project
def update_project id, body
project = Devops::Model::Project.new(body)
project.id = id
old_project = Devops::Db.connector.project id
Devops::Db.connector.project_update project
roles = create_new_roles(old_project, project, logger)
create_roles_response(roles)
end
# TODO: multi project
def update_project_users id, deploy_env, users
project = Devops::Db.connector.project(id)
dbusers = Devops::Db.connector.users(users).map{|u| u.id}
buf = dbusers - users
project.add_authorized_user users, deploy_env
Devops::Db.connector.project_update(project)
info = "Users '#{dbusers.join("', '")}' has been added to '#{id}' project's authorized users"
info << ", invalid users: '#{buf.join("', '")}'" unless buf.empty?
info
end
# TODO: multi project
def delete_project_users id, deploy_env, users
project = Devops::Db.connector.project(id)
project.remove_authorized_user users, deploy_env
Devops::Db.connector.project_update project
"Users '#{users.join("', '")}' have been removed from '#{id}' project's authorized users"
end
# TODO: multi project
def set_project_env_run_list id, deploy_env, list
project = Devops::Db.connector.project(id)
env = project.deploy_env deploy_env
env.run_list = list
Devops::Db.connector.project_update project
"Updated environment '#{env.identifier}' with run_list '#{env.run_list.inspect}' in project '#{project.id}'"
end
def delete_project id, deploy_env
servers = Devops::Db.connector.servers id
raise DependencyError.new "Deleting #{id} is forbidden: Project has servers" if !servers.empty?
project = Devops::Db.connector.project(id)
info = if deploy_env.nil?
project.delete
"Project '#{id}' is deleted"
else
project.remove_env deploy_env
Devops::Db.connector.project_update project
"Project '#{id}'. Deploy environment '#{deploy_env}' has been deleted"
end
end
def deploy_project_stream out, id, deploy_env, servers, body
keys = {}
dbserver = servers(id, deploy_env, servers)
out << (dbservers.empty? ? "No reserved servers to deploy\n" : "Deploy servers: '#{dbservers.map{|s| s.chef_node_name}.join("', '")}'\n")
status = []
servers.each do |s|
begin
Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
out << e.message + "\n"
status.push 2
next
end
unless keys.key? s.key
k = Devops::Db.connector.key s.key
keys[s.key] = k.path
end
status.push(deploy_server(out, s, keys[s.key]))
end
status
end
def deploy_project id, deploy_env, servers, body
dir = DevopsConfig[:report_dir_v2]
files = []
uri = URI.parse(request.url)
servers(id, deploy_env, servers).each do |s|
project = begin
Devops::Db.connector.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
next
end
jid = DeployWorker.perform_async(dir, s.to_hash, [], DevopsConfig.config)
#logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s
end
files
end
def servers project_id, deploy_env, servers
project = Devops::Db.connector.project(project_id)
dbservers = Devops::Db.connector.servers(project_id, deploy_env, servers, true)
end
def archive_project id
project = Devops::Db.connector.project(id)
Devops::Db.connector.archive_project(id)
"Project '#{id}' has been archived"
end
def unarchive_project id
project = Devops::Db.connector.project(id)
Devops::Db.connector.unarchive_project(id)
"Project '#{id}' has been unarchived"
end
def test_project id, deploy_env
project = Devops::Db.connector.project(id)
env = project.deploy_env deploy_env
#logger.info "Test project '#{project.id}' and environment '#{env.identifier}'"
if env.provider == ::Provider::Static::PROVIDER
msg = "Can not test environment with provider '#{::Provider::Static::PROVIDER}'"
Logger.warn msg
raise InvalidRecord.new(msg)
end
dir = DevopsConfig[:report_dir_v2]
uri = URI.parse(request.url)
p = {
:project => project.id,
:env => env.identifier,
:user => @request.env['REMOTE_USER']
}
jid = ProjectTestWorker.perform_async(dir, p, DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE
#logger.info "Job '#{jid}' has been created"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
sleep 1
return [uri.to_s]
end
def create_roles project_id, envs, logger
all_roles = KnifeCommands.roles
return " Can't get roles list" if all_roles.nil?
roles = {:new => [], :error => [], :exist => []}
envs.each do |e|
role_name = KnifeCommands.role_name(project_id, e.identifier)
begin
if all_roles.include? role_name
roles[:exist].push role_name
else
KnifeCommands.create_role role_name, project_id, e.identifier
roles[:new].push role_name
logger.info "Role '#{role_name}' created"
end
rescue => er
roles[:error].push role_name
logger.error "Role '#{role_name}' can not be created: #{er.message}"
end
end
roles
end
def create_new_roles old_project, new_project, logger
old_project.deploy_envs.each do |e|
new_project.remove_env(e.identifier)
end
create_roles new_project.id, new_project.deploy_envs, logger
end
def create_roles_response roles
if roles.is_a?(String)
roles
else
info = ""
info += " Project roles '#{roles[:new].join("', '")}' have been automaticaly created" unless roles[:new].empty?
info += " Project roles '#{roles[:exist].join("', '")}' weren't created because they exist" unless roles[:exist].empty?
info += " Project roles '#{roles[:error].join("', '")}' weren't created because of internal error" unless roles[:error].empty?
info
end
end
end
end
end
end

View File

@ -0,0 +1,16 @@
require "providers/provider_factory"
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Provider < RequestHandler
def providers
::Provider::ProviderFactory.providers
end
end
end
end
end

View File

@ -0,0 +1,64 @@
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Report < RequestHandler
def initialize request, params
@params = params
end
def options
options = {}
["project", "deploy_env", "type", "created_by", "date_from", "date_to", "sort", "status", "chef_node_name", "max_number"].each do |k|
options[k] = @params[k] unless @params[k].nil?
end
attributes_keys = @params.keys.select{|k| k =~ /attributes\.*/}
attributes_keys.each do |ak|
options[ak] = @params[ak]
end
options
end
def all
Devops::Db.connector.reports(options)
end
def all_latest
Devops::Db.connector.latest_reports(options())
end
def attributes name
Devops::Db.connector.reports_attributes_values(name)
end
def report id
r = Devops::Db.connector.report(id)
file = r.file
raise RecordNotFound.new("Report '#{id}' does not exist") unless File.exists? file
return Rack::Utils.escape_html(File.read(file)), completed?(id)
rescue RecordNotFound => e
if status(id) == Worker::STATUS::IN_QUEUE
return "Task '#{id}' has been queued", false
else
raise e
end
end
def status id
Sidekiq.redis do |connection|
connection.hget("devops", id)
end
end
def completed? id
r = self.status(id)
r == "completed" or r == "failed"
end
end
end
end
end

View File

@ -0,0 +1,12 @@
module Devops
module API2_0
module Handler
class RequestHandler
def initialize request, params
@request = request
@params = params
end
end
end
end
end

View File

@ -0,0 +1,98 @@
require "providers/provider_factory"
require "fileutils"
require "commands/status"
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Script < RequestHandler
def scripts
res = []
Dir.foreach(DevopsConfig.config[:scripts_dir]) {|f| res.push(f) unless f.start_with?(".")}
end
def execute_command out, cmd, node_name
user = @request.env['REMOTE_USER']
s = ::Devops::Db.connector.server_by_chef_node_name node_name
::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user
cert = ::Devops::Db.connector.key s.key
addr = "#{s.remote_user}@#{s.public_ip || s.private_ip}"
ssh_cmd = "ssh -i %s #{addr} '#{cmd}'"
out << ssh_cmd % File.basename(cert.path)
out << "\n"
IO.popen((ssh_cmd % cert.path) + " 2>&1") do |so|
while line = so.gets do
out << line
end
end
out << "\nDone"
end
def run_script out, file_name, nodes, script_params
file = File.join(DevopsConfig.config[:scripts_dir], file_name)
unless File.exists?(file)
out << "File '#{file_name}' does not exist\n"
return
end
servers = ::Devops::Db.connector.servers_by_names(nodes)
if servers.empty?
out << "No servers found for names '#{nodes.join("', '")}'\n"
return
end
user = @request.env['REMOTE_USER']
servers.each do |s|
::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user
end
status = []
servers.each do |s|
cert = begin
::Devops::Db.connector.key s.key
rescue
out << "No key found for '#{s.chef_node_name}'"
status.push 2
next
end
ssh_cmd = "ssh -i #{cert.path} #{s.remote_user}@#{s.public_ip || s.private_ip} 'bash -s' < %s"
out << "\nRun script on '#{s.chef_node_name}'\n"
unless script_params.nil?
ssh_cmd += " " + script_params.join(" ")
end
out << (ssh_cmd % [file_name])
out << "\n"
begin
IO.popen( (ssh_cmd % [file]) + " 2>&1") do |so|
while line = so.gets do
out << line
end
so.close
status.push $?.to_i
end
rescue IOError => e
logger.error e.message
out << e.message
status.push 3
end
end
out << create_status(status)
end
def create_script file_name
file = File.join(settings.scripts_dir, file_name)
raise RecordNotFound.new("File '#{file_name}' already exist") if File.exists?(file)
File.open(file, "w") {|f| f.write(@request.body.read)}
end
def delete_script file_name
file = File.join(settings.scripts_dir, file_nsme)
raise RecordNotFound.new("File '#{file_name}' does not exist", 404) unless File.exists?(file)
FileUtils.rm(file)
end
end
end
end
end

View File

@ -0,0 +1,269 @@
require 'rufus-scheduler'
require "uri"
require "commands/status"
require "commands/server"
require "commands/bootstrap_templates"
require "commands/knife_commands"
require "providers/provider_factory"
require "db/mongo/models/server"
require "workers/create_server_worker"
require "workers/bootstrap_worker"
module Devops
module API2_0
module Handler
class Server
extend StatusCommands
extend ServerCommands
extend BootstrapTemplatesCommands
scheduler = Rufus::Scheduler.new
def servers fields, reserved
Devops::DB.connector.servers(nil, nil, nil, reserved, fields).map {|s| s.to_hash}
end
def chef_servers
KnifeCommands.chef_node_list
end
def provider_servers provider
::Provider::ProviderFactory.get(provider).servers
end
def server id
get_server_by_key(id, @params[:key])
end
def delete id, key
s = get_server_by_key(params[:id], key)
### Authorization
Devops::Db.connector.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
delete_server(s, Devops::Db.connector, logger)
end
def create_server_stream out, body
status = []
prepare_create_server(body).each do |s|
res = create_server_proc.call(out, s, provider, Devops::Db.connector)
status.push res
end
status
end
def create_server body
dir = DevopsConfig[:report_dir_v2]
files = []
uri = URI.parse(request.url)
prepare_create_server(body).each do |s|
h = s.to_hash
h["options"] = s.options
jid = CreateServerWorker.perform_async(dir, env.provider, h, @request.env['REMOTE_USER'], DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE
#logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s
end
sleep 1
files
end
def prepare_create_server body
user = @request.env['REMOTE_USER']
key_name = body["key"]
new_key = Devops::Db.connector.key(key_name) unless key_name.nil?
p = Devops::Db.connector.check_project_auth(body["project"], body["deploy_env"], user)
env = p.deploy_env(body["deploy_env"])
provider = ::Provider::ProviderFactory.get(env.provider)
server_name = body["name"]
check_chef_node_name(server_name, provider) unless server_name.nil?
groups = body["groups"]
unless groups.nil?
buf = groups - provider.groups.keys
halt_response("Invalid security groups '#{buf.join("', '")}' for provider '#{provider.name}'") if buf.empty?
end
Server.extract_servers(provider, p, env, body, user, Devops::Db.connector)
end
def pause_server node_name, key
s = Server.get_server_by_key(node_name, key)
## Authorization
Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
provider = ::Provider::ProviderFactory.get(s.provider)
r = provider.pause_server s
if r.nil?
"Server with instance ID '#{s.id}' and node name '#{node_name}' is paused"
else
raise ConflictException.new("Server with instance ID '#{s.id}' and node name '#{node_name}' can not be paused, It in state '#{r}'", 409)
end
end
def unpause_server node_name, key
s = Server.get_server_by_key(params[:node_name], key)
## Authorization
Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
provider = ::Provider::ProviderFactory.get(s.provider)
r = provider.unpause_server s
if r.nil?
"Server with instance ID '#{s.id}' and node name '#{node_name}' is unpaused"
else
raise ConflictException.new("Server with instance ID '#{s.id}' and node name '#{node_name}' can not be unpaused, It in state '#{r}'")
end
end
def reserve_server node_name, key
s = get_server_by_key(node_name, key)
Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
raise ConflictException.new("Server '#{node_name}' already reserved") unless s.reserved_by.nil?
s.reserved_by = user
Devops::Db.connector.server_update(s)
end
def unreserve_server node_name, key
s = get_server_by_key(node_name, key)
Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
raise ConflictException.new("Server '#{node_name}' is not reserved") if s.reserved_by.nil?
s.reserved_by = nil
Devops::Db.connector.server_update(s)
end
# TODO: check bootstrap template name
def bootstrap_server_stream out, body
s = prepare_create_server body
status = []
cert = Devops::Db.connector.key s.key
logger.debug "Bootstrap certificate path: #{cert.path}"
bootstrap s, out, cert.path, logger
str = nil
r = if check_server(s)
Devops::Db.connector.server_set_chef_node_name s
str = "Server with id '#{s.id}' is bootstraped"
logger.info str
0
else
str = "Server with id '#{s.id}' is not bootstraped"
logger.warn str
1
end
status.push r
out << str
out << "\n"
status
end
def bootstrap_server body
s = prepare_create_server body
dir = DevopsConfig[:report_dir_v2]
files = []
uri = URI.parse(@request.url)
h = s.to_hash
h["options"] = s.options
h["_id"] = s.id
jid = BootstrapWorker.perform_async(dir, d.provider, h, @request.env['REMOTE_USER'], DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE
logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
uri.query = nil
uri.fragment = nil
files.push uri.to_s
sleep 1
files
end
def prepare_bootstrap_server body
id = body["instance_id"]
name = body["name"]
s = Devops::Db.connector.server_by_instance_id(id)
p = Devops::Db.connector.check_project_auth s.project, s.deploy_env, @request.env['REMOTE_USER']
d = p.deploy_env s.deploy_env
provider = ::Provider::ProviderFactory.get(s.provider)
check_chef_node_name(name, provider) unless name.nil?
s.options = {
:run_list => rl || d.run_list,
}
unless t.nil?
templates = get_templates
halt_response("Invalid bootstrap template '#{t}', available values: #{templates.join(", ")}", 400) unless templates.include?(t)
s.options[:bootstrap_template] = t
end
s.chef_node_name = name || provider.create_default_chef_node_name(s)
s
end
def add_server body
project = body["project"]
deploy_env = body["deploy_env"]
p = Devops::Db.connector.check_project_auth project, deploy_env, @request.env['REMOTE_USER']
d = p.deploy_env(deploy_env)
cert = Devops::Db.connector.key(body["key"])
provider = ::Provider::ProviderFactory.get("static")
s = Devops::Model::Server.new
s.provider = provider.name
s.project = project
s.deploy_env = deploy_env
s.remote_user = body["remote_user"]
s.private_ip = body["private_ip"]
s.public_ip = body["public_ip"]
s.static = true
s.id = "static_#{cert.id}-#{Time.now.to_i}"
s.key = cert.id
Devops::Db.connector.server_insert s
"Server '#{s.id}' has been added"
end
def get_server_by_key id, key
mongo = Devops::Db.connector
key == "instance" ? mongo.server_by_instance_id(id) : mongo.server_by_chef_node_name(id)
end
def check_chef_node_name name, provider
mongo = Devops::Db.connector
mongo.server_by_chef_node_name name
halt(400, "Server with name '#{name}' already exist")
rescue RecordNotFound => e
# server not found - OK
s = provider.servers.detect {|s| s["name"] == name}
halt(400, "#{provider.name} node with name '#{name}' already exist") unless s.nil?
s = KnifeCommands.chef_node_list.detect {|n| n == name}
halt(400, "Chef node with name '#{name}' already exist") unless s.nil?
s = KnifeCommands.chef_client_list.detect {|c| c == name}
halt(400, "Chef client with name '#{name}' already exist") unless s.nil?
end
end
class ExpireHandler
include ServerCommands
def initialize server, logger
@server = server
@logger = logger
end
def call(job)
@logger.info("Removing node '#{@server.chef_node_name}' form project '#{@server.project}' and env '#{@server.deploy_env}'")
begin
delete_server(@server, Devops::Db.connector, @logger)
rescue => e
logger.error "ExpiredHandler error: " + e.message
end
end
end
end
end
end

View File

@ -0,0 +1,51 @@
require 'db/mongo/models/stack/stack_factory'
require_relative "request_handler"
module Devops
module API2_0
module Handler
class Stack < RequestHandler
def stacks
Devops::Db.connector.stacks
end
def stacks_for_provider provider
Devops::Db.connector.stacks(provider)
end
def create_stack object
stack_model = Model::StackFactory.create(object['provider'], object)
Devops::Db.connector.stack_insert(stack_model)
stack_model
end
def stack id
Devops::Db.connector.stack(id)
end
def delete_stack id
stack = self.stack(id)
stack.delete_stack_in_cloud!
Devops::Db.connector.stack_delete(id)
end
def sync_details id
stack = self.stack(id)
stack.sync_details!
Devops::Db.connector.stack_update(stack)
end
def resources id
stack = Devops::Db.connector.stack(id)
stack.resources
end
def resources id, resource_id
stack = Devops::Db.connector.stack(id)
stack.resource(resource_id)
end
end
end
end
end

View File

@ -0,0 +1,28 @@
require 'json'
require 'lib/stack_presets/factory'
require_relative "request_handler"
module Devops
module API2_0
module Handler
class StackPreset < RequestHandler
def presets
Devops::StackPresetsFactory.list
end
def preset id
Devops::StackPresetsFactory.get(id)
end
def apply id, body
preset = Devops::StackPresetsFactory.get(id)
preset.create_stack_from_preset(body)
Devops::Db.connector.stack_insert(stack)
end
end
end
end
end

View File

@ -0,0 +1,34 @@
require 'db/mongo/models/stack_template/stack_template_factory'
require_relative "request_handler"
module Devops
module API2_0
module Handler
class StackTemplate < RequestHandler
def stack_templates
Devops::Db.connector.stack_templates
end
def stack_templates_for_provider provider
Devops::Db.connector.stack_templates(provider)
end
def create_stack_template body
template_model = Model::StackTemplateFactory.create(body['provider'], body)
Devops::Db.connector.stack_template_insert(template_model)
template_model
end
def get_stack_template id
Devops::Db.connector.stack_template(id)
end
def delete_stack_template id
Devops::Db.connector.stack_template_delete id
end
end
end
end
end

View File

@ -0,0 +1,34 @@
require "commands/knife_commands"
module Devops
module API2_0
module Handler
class Tag
def initialize node_name
@node_name = node_name
@server = Devops::DB.connector.server_by_chef_node_name(@node_name)
#TODO: raise
halt_response("No servers found for name '#{@node_name}'", 404) if @server.nil?
end
def tags
KnifeCommands.tags_list(@node_name)
end
def set_tags tags
tagsStr = tags.join(" ")
cmd = KnifeCommands.tags_create(@node_name, tagsStr)
halt_response("Error: Cannot add tags #{tagsStr} to server #{@node_name}", 500) unless cmd[1]
end
def self.unset_tags
tagsStr = tags.join(" ")
cmd = KnifeCommands.tags_delete(@node_name, tagsStr)
halt_response("Cannot delete tags #{tagsStr} from server #{@node_name}: #{cmd[0]}", 500) unless cmd[1]
end
end
end
end
end

View File

@ -0,0 +1,47 @@
require "db/mongo/models/user"
module Devops
module API2_0
module Handler
class User
def users
Devops::Db.connector.users
end
def create body
Devops::Db.connector.user_insert Devops::Model::User.new(body)
end
def delete user_id
Devops::Db.connector.user_delete user_id
end
def change_user_privileges user_id, cmd, privileges
change_user(user_id) do
user.grant(cmd, privileges)
end
end
def change_email user_id, val
change_user(user_id) do
user.email = val
end
end
def change_password user_id, val
change_user(user_id) do
user.password = val
end
end
def change_user user_id
user = Devops::Db.connector.user user_id
yield(user)
Devops::Db.connector.user_update user
end
end
end
end
end

View File

@ -5,7 +5,7 @@ require "sinatra/json"
require "providers/provider_factory"
module Devops
module Version2_0
module API2_0
module Helpers
def create_response msg, obj=nil, rstatus=200
@ -23,11 +23,10 @@ module Devops
def check_privileges cmd, p
# somewhy REMOTE_USER is missing
user = request.env['HTTP_REMOTE_USER'] || request.env['REMOTE_USER']
settings.mongo.check_user_privileges(user, cmd, p)
user = request.env['USER']
user.check_privileges(cmd, p)
end
def check_provider provider
list = ::Provider::ProviderFactory.providers
halt_response("Invalid provider '#{provider}', available providers: '#{list.join("', '")}'", 404) unless list.include?(provider)

View File

@ -1,5 +1,5 @@
module Devops
module Version2_0
module API2_0
module Routes
module BootstrapTemplatesRoutes
@ -18,7 +18,10 @@ module Devops
# [
# "omnibus"
# ]
app.get_with_headers "/templates", :headers => [:accept], &Devops::Version2_0::Handler::BootstrapTemplates.get_bootstrap_templates
app.get_with_headers "/templates", :headers => [:accept] do
check_privileges("templates", "r")
json Devops::API2_0::Handler::BootstrapTemplates.new.get_templates
end
puts "Bootstrap templates routes initialized"
end

View File

@ -0,0 +1,60 @@
module Devops
module API2_0
module Routes
module DeployRoutes
def self.registered(app)
# Run chef-client on reserved server
#
# * *Request*
# - method : POST
# - headers :
# - Content-Type: application/json
# - body :
# {
# "names": [], -> array of servers names to run chef-client
# "tags": [], -> array of tags to apply on each server before running chef-client
# "trace": true -> return output in stream
# }
#
# * *Returns* : text stream
app.post_with_headers "/deploy", :headers => [:content_type] do
check_privileges("server", "x")
# TODO: send message
#broadcast(:devops_deploy, "deploy")
r = create_object_from_json_body
names = check_array(r["names"], "Parameter 'names' should be a not empty array of strings")
tags = check_array(r["tags"], "Parameter 'tags' should be an array of strings", String, true) || []
if r.key?("trace")
stream() do |out|
status = []
begin
Devops::API2_0::Handler::Deploy.new(request, params).deploy_stream(out, names, tags)
rescue IOError => e
logger.error e.message
break
end
end # stream
else
ids = Devops::API2_0::Handler::Deploy.new(request, params).deploy(names, tags)
sleep 1
ids.each do |jid|
logger.info "Job '#{jid}' has been queued"
uri.path = "#{DevopsConfig.config[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s
end
json files
end
end
puts "Deploy routes initialized"
end
end
end
end
end

View File

@ -1,20 +1,10 @@
module Devops
module Version2_0
module API2_0
module Routes
module FilterRoutes
def self.registered(app)
app.before "/filter/:provider/image" do
protect!
check_headers :accept, :content_type
check_privileges("filter", "w")
check_provider(params[:provider])
@images = create_object_from_json_body(Array)
halt_response("Request body should not be an empty array") if @images.empty?
check_array(@images, "Request body should contains an array with strings")
end
# Get list of images filters for :provider
#
# Devops can works with images from filters list only
@ -33,7 +23,11 @@ module Devops
# [
# "36dc7618-4178-4e29-be43-286fbfe90f50"
# ]
app.get_with_headers "/filter/:provider/images", :headers => [:accept], &Devops::Version2_0::Handler::Filter.get_filters
app.get_with_headers "/filter/:provider/images", :headers => [:accept] do |provider|
check_privileges("filter", "r")
check_provider(provider)
json Devops::API2_0::Handler::Filter.new(request, params).available_images(provider)
end
hash = {}
@ -50,7 +44,14 @@ module Devops
# ] -> array of image ids to add to filter
#
# * *Returns* : list of images filters for :provider
hash["PUT"] = Devops::Version2_0::Handler::Filter.add_filter
hash["PUT"] = lambda { |provider|
check_privileges("filter", "w")
check_provider(provider)
images = create_object_from_json_body(Array)
halt_response("Request body should not be an empty array") if images.empty?
check_array(images, "Request body should contains an array with strings")
create_response("Updated", {:images => Devops::API2_0::Handler::Filter.new(request, params).add_images(images, provider)})
}
# Delete image ids from filter for :provider
#
@ -65,7 +66,14 @@ module Devops
# ] -> array of image ids to delete from filter
#
# * *Returns* : list of images filters for :provider
hash["DELETE"] = Devops::Version2_0::Handler::Filter.delete_filter
hash["DELETE"] = lambda { |provider|
check_privileges("filter", "w")
check_provider(provider)
images = create_object_from_json_body(Array)
halt_response("Request body should not be an empty array") if images.empty?
check_array(images, "Request body should contains an array with strings")
create_response("Deleted", {:images => Devops::API2_0::Handler::Filter.new(request, params).delete_images(images, provider)})
}
app.multi_routes "/filter/:provider/image", {:headers => [:accept, :content_type]}, hash

View File

@ -1,5 +1,5 @@
module Devops
module Version2_0
module API2_0
module Routes
module FlavorRoutes
@ -31,7 +31,12 @@ module Devops
# "disk": 20
# }
# ]
app.get_with_headers "/flavors/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Flavor.get_flavors
app.get_with_headers "/flavors/:provider", :headers => [:accept] do
check_privileges("flavor", "r")
provider = params[:provider]
check_provider(provider)
json Devops::API2_0::Handler::Flavor.new(request, params).flavors
end
puts "Flavor routes initialized"
end

View File

@ -1,6 +1,6 @@
# encoding: UTF-8
module Devops
module Version2_0
module API2_0
module Routes
module GroupRoutes
@ -44,7 +44,12 @@ module Devops
# }
# }
# TODO: vpc support for ec2
app.get_with_headers "/groups/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Group.get_groups
app.get_with_headers "/groups/:provider", :headers => [:accept] do#, &Devops::Version2_0::Handler::Group.get_groups
check_privileges("group", "r")
provider = params[:provider]
check_provider(provider)
json Devops::API2_0::Handler::Group.new(provider).groups(params)
end
puts "Group routes initialized"
end

View File

@ -1,12 +1,9 @@
module Devops
module Version2_0
module API2_0
module Routes
module ImageRoutes
def self.registered(app)
app.after %r{\A/image(/[\w]+)?\z} do
statistic
end
# Get devops images list
#
@ -27,7 +24,12 @@ module Devops
# "id": "36dc7618-4178-4e29-be43-286fbfe90f50"
# }
# ]
app.get_with_headers "/images", :headers => [:accept], &Devops::Version2_0::Handler::Image.get_images
app.get_with_headers "/images", :headers => [:accept] do
check_privileges("image", "r")
provider = params[:provider]
check_provider(provider) if provider
json Devops::API2_0::Handler::Image.new(request, params).images(provider).map(&:to_hash)
end
# Get raw images for :provider
#
@ -53,7 +55,11 @@ module Devops
# "status": "ACTIVE"
# }
# ]
app.get_with_headers "/images/provider/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Image.get_provider_images
app.get_with_headers "/images/provider/:provider", :headers => [:accept] do |provider|
check_privileges("image", "r")
check_provider(provider)
json Devops::API2_0::Handler::Image.new(request, params).provider_images(provider)
end
# Create devops image
#
@ -73,7 +79,12 @@ module Devops
#
# * *Returns* :
# 201 - Created
app.post_with_headers "/image", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Image.create_image
app.post_with_headers "/image", :headers => [:accept, :content_type] do
check_privileges("image", "w")
image = create_object_from_json_body
Devops::API2_0::Handler::Image.new(request, params).create_image(image)
create_response "Created", nil, 201
end
hash = {}
# Get devops image by id
@ -91,7 +102,10 @@ module Devops
# "bootstrap_template": null,
# "id": "36dc7618-4178-4e29-be43-286fbfe90f50"
# }
hash["GET"] = Devops::Version2_0::Handler::Image.get_image
hash["GET"] = lambda { |image_id|
check_privileges("image", "r")
json Devops::API2_0::Handler::Image.new(request, params).image(image_id)
}
# Update devops image
#
@ -111,7 +125,12 @@ module Devops
#
# * *Returns* :
# 200 - Updated
hash["PUT"] = Devops::Version2_0::Handler::Image.update_image
hash["PUT"] = lambda {|image_id|
check_privileges("image", "w")
image = create_object_from_json_body
Devops::API2_0::Handler::Image.new(request, params).update_image(image_id, image)
create_response("Image '#{image_id}' has been updated")
}
# Delete devops image
#
@ -122,7 +141,11 @@ module Devops
#
# * *Returns* :
# 200 - Deleted
hash["DELETE"] = Devops::Version2_0::Handler::Image.delete_image
hash["DELETE"] = lambda {|image_id|
check_privileges("image", "w")
Devops::API2_0::Handler::Image.delete_image
create_response("Image '#{image_id}' has been removed")
}
app.multi_routes "/image/:image_id", {}, hash

View File

@ -0,0 +1,93 @@
require "json"
module Devops
module API2_0
module Routes
module KeyRoutes
def self.registered(app)
# Get list of available ssh keys
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of strings
# [
# {
# "scope": "system", -> 'system' - key was added by server, 'user' - key was added by user
# "id": "devops"
# }
# ]
app.get_with_headers "/keys", :headers => [:accept] do
check_privileges("key", "r")
json Devops::API2_0::Handler::Key.new.keys
end
# Create ssh key on devops server
#
# * *Request*
# - method : POST
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# {
# "file_name": "key file name",
# "key_name": "key name",
# "content": "key content"
# }
#
# * *Returns* :
# 201 - Created
app.post_with_headers "/key", :headers => [:accept, :content_type] do
check_privileges("key", "w")
key = create_object_from_json_body
fname = check_filename(key["file_name"], "Parameter 'file_name' must be a not empty string")
kname = check_string(key["key_name"], "Parameter 'key_name' should be a not empty string")
content = check_string(key["content"], "Parameter 'content' should be a not empty string")
file_name = File.join(settings.keys_dir, fname)
halt(400, "File '#{fname}' already exist") if File.exists?(file_name)
Devops::API2_0::Handler::Key.new.create(key, file_name)
create_response("Created", nil, 201)
end
# Delete ssh key from devops server
#
# * *Request*
# - method : DELETE
# - headers :
# - Accept: application/json
#
# * *Returns* :
# 200 - Deleted
app.delete_with_headers "/key/:key", :headers => [:accept] do
check_privileges("key", "w")
servers = settings.mongo.servers_by_key params[:key]
unless servers.empty?
s_str = servers.map{|s| s.id}.join(", ")
raise DependencyError.new "Deleting is forbidden: Key is used in servers: #{s_str}"
end
k = settings.mongo.key params[:key]
begin
FileUtils.rm(k.path)
rescue
logger.error "Missing key file for #{params[:key]} - #{k.filename}"
end
r = settings.mongo.key_delete params[:key]
r = Devops::API2_0::Handler::Key.new.delete params[:key]
return [500, r["err"].inspect] unless r["err"].nil?
create_response("Key '#{params[:key]}' removed")
end
puts "Key routes initialized"
end
end
end
end
end

View File

@ -1,6 +1,6 @@
# encoding: UTF-8
module Devops
module Version2_0
module API2_0
module Routes
module NetworkRoutes
@ -32,7 +32,11 @@ module Devops
# "id": "b14f8df9-ac27-48e2-8d65-f7ef78dc2654"
# }
# ]
app.get_with_headers "/networks/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Network.get_networks
app.get_with_headers "/networks/:provider", :headers => [:accept] do |provider|
check_privileges("network", "r")
check_provider(provider)
json Devops::API2_0::Handler::Network.new(request, params).networks
end
puts "Network routes initialized"
end

View File

@ -1,5 +1,5 @@
module Devops
module Version2_0
module API2_0
module Routes
module ProjectRoutes
@ -18,7 +18,10 @@ module Devops
# [
# {"name" : "project_1"}
# ]
app.get_with_headers "/projects", :headers => [:accept], &Devops::Version2_0::Handler::Project.get_projects
app.get_with_headers "/projects", :headers => [:accept] do
check_privileges("project", "r")
json Devops::API2_0::Handler::Project.new(request, params).projects.map(&:to_hash)
end
# Get project by id
#
@ -53,7 +56,10 @@ module Devops
# "name": "project_1"
# }
hash = {}
hash["GET"] = Devops::Version2_0::Handler::Project.get_project
hash["GET"] = lambda {|project|
check_privileges("project", "r")
json Devops::API2_0::Handler::Project.new(request, params).project(project)
}
# Update project and create chef roles
#
@ -90,7 +96,13 @@ module Devops
#
# * *Returns* :
# 200 - Updated
hash["PUT"] = Devops::Version2_0::Handler::Project.update_project
hash["PUT"] = lambda { |project|
check_privileges("project", "w")
body = create_object_from_json_body
r = Devops::API2_0::Handler::Project.new(request, params).update_project project, body
info = "Project '#{project.id}' has been updated." + r
create_response(info)
}
# Delete project
#
@ -106,7 +118,15 @@ module Devops
#
# * *Returns* :
# 200 - Deleted
hash["DELETE"] = Devops::Version2_0::Handler::Project.delete_project
hash["DELETE"] = lambda {|project|
check_privileges("project", "w")
body = create_object_from_json_body(Hash, true)
deploy_env = unless body.nil?
check_string(body["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true)
end
info = Devops::API2_0::Handler::Project.new(request, params).delete_project(project, deploy_env)
create_response(info)
}
app.multi_routes "/project/:project", {}, hash
# Get project servers
@ -135,7 +155,10 @@ module Devops
# "id": "nstance id"
# }
# ]
app.get_with_headers "/project/:project/servers", :headers => [:accept], &Devops::Version2_0::Handler::Project.get_project_servers
app.get_with_headers "/project/:project/servers", :headers => [:accept] do |project|
check_privileges("project", "r")
json Devops::API2_0::Handler::Project.new(request, params).project_servers(project).map(&:to_hash)
end
# Get project stacks
#
@ -199,7 +222,15 @@ module Devops
#
# * *Returns* :
# 201 - Created
app.post_with_headers "/project", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Project.create_project
app.post_with_headers "/project", :headers => [:accept, :content_type] do
check_privileges("project", "w")
body = create_object_from_json_body
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)
res = Devops::API2_0::Handler::Project.new(request, params).create_project body
res = "Created. " + res
create_response(res, nil, 201)
end
users_hash = {}
# Add users to project environment
@ -219,7 +250,14 @@ module Devops
#
# * *Returns* :
# 200 - Updated
users_hash["PUT"] = Devops::Version2_0::Handler::Project.update_project_users
users_hash["PUT"] = lambda { |project|
check_privileges("project", "w")
body = create_object_from_json_body
users = check_array(body["users"], "Parameter 'users' must be a not empty array of strings")
deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string", true)
info = Devops::API2_0::Handler::Project.new(request, params).update_project_users(project, deploy_env, users)
create_response(info)
}
# Delete users from project environment
#
@ -238,7 +276,14 @@ module Devops
#
# * *Returns* :
# 200 - Updated
users_hash["DELETE"] = Devops::Version2_0::Handler::Project.delete_project_users
users_hash["DELETE"] = lambda {|project|
check_privileges("project", "w")
body = create_object_from_json_body
users = check_array(body["users"], "Parameter 'users' must be a not empty array of strings")
deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string", true)
info = Devops::API2_0::Handler::Project.new(request, params).delete_project_users(project, deploy_env, users)
create_response(info)
}
app.multi_routes "/project/:id/user", {}, users_hash
# Set run_list to project environment
@ -256,7 +301,13 @@ module Devops
#
# * *Returns* :
# 200 - Updated
app.put_with_headers "/project/:id/:env/run_list", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Project.set_project_env_run_list
app.put_with_headers "/project/:id/:env/run_list", :headers => [:accept, :content_type] do |project, deploy_env|
check_privileges("project", "w")
list = create_object_from_json_body(Array)
check_array(list, "Body must contains not empty array of strings")
info = Devops::API2_0::Handler::Project.new(request, params).set_project_env_run_list(project, deploy_env, list)
create_response(info)
end
# Run chef-client on reserved project servers
#
@ -273,7 +324,25 @@ module Devops
# }
#
# * *Returns* : text stream
app.post_with_headers "/project/:id/deploy", :headers => [:content_type], &Devops::Version2_0::Handler::Project.deploy_project
app.post_with_headers "/project/:id/deploy", :headers => [:content_type] do |project|
check_privileges("project", "x")
obj = create_object_from_json_body
deploy_env = check_string(obj["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true)
servers = check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true)
handler = Devops::API2_0::Handler::Project.new(request, params)
if obj.key?("trace")
stream() do |out|
begin
status = handler.deploy_project_stream out, project, deploy_env, servers
out << create_status(status)
rescue IOError => e
logger.error e.message
end
end
else
json handler.deploy_project project, deploy_env, servers
end
end
# Set project to archivated state
#
@ -281,7 +350,11 @@ module Devops
# - method : POST
# - headers :
# - Content-Type: application/json
app.post_with_headers "/project/:project/archive", :headers => [:content_type], &Devops::Version2_0::Handler::Project.archive_project
app.post_with_headers "/project/:project/archive", :headers => [:content_type] do |project|
check_privileges("project", "w")
info = Devops::API2_0::Handler::Project.new(request, params).archive_project(project)
create_response(info)
end
# Set project to normal state
#
@ -289,7 +362,11 @@ module Devops
# - method : POST
# - headers :
# - Content-Type: application/json
app.post_with_headers "/project/:project/unarchive", :headers => [:content_type], &Devops::Version2_0::Handler::Project.unarchive_project
app.post_with_headers "/project/:project/unarchive", :headers => [:content_type] do |project|
check_privileges("project", "w")
info = Devops::API2_0::Handler::Project.new(request, params).unarchive_project(project)
create_response(info)
end
# Test project environment
#
@ -357,7 +434,10 @@ module Devops
# },
# "message": "Test project 'project_1' and environment 'prod'"
# }
app.post_with_headers "/project/test/:id/:env", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Project.test_project
app.post_with_headers "/project/test/:id/:env", :headers => [:accept, :content_type] do |project, deploy_env|
check_privileges("project", "r")
json Devops::API2_0::Handler::Project.new(request, params).test_project(project, deploy_env)
end
puts "Project routes initialized"
end

View File

@ -1,10 +1,9 @@
# encoding: UTF-8
require "json"
require "providers/provider_factory"
module Devops
module Version2_0
module API2_0
module Routes
module ProviderRoutes
@ -22,7 +21,10 @@ module Devops
# "ec2",
# "openstack"
# ]
app.get_with_headers "/providers", :headers => [:accept], &Devops::Version2_0::Handler::Provider.get_providers
app.get_with_headers "/providers", :headers => [:accept] do#, &Devops::Version2_0::Handler::Provider.get_providers
check_privileges("provider", "r")
json Devops::API2_0::Handler::Provider.new(request, params).providers
end
puts "Provider routes initialized"
end

View File

@ -0,0 +1,61 @@
module Devops
module API2_0
module Routes
module ReportRoutes
def self.registered(app)
app.get_with_headers "/report/all", headers: [:accept] do
json Devops::API2_0::Handler::Report.new(request, params).all.map{|r| r.to_hash}
end
app.get_with_headers "/report/all/latest", headers: [:accept] do
json Devops::API2_0::Handler::Report.new(request, params).all_latest.map{|r| r.to_hash}
end
app.get_with_headers "/report/all/attributes/:name", headers: [:accept] do |name|
json Devops::API2_0::Handler::Report.new(request, params).attributes(name)
end
app.get_with_headers "/report/:id", headers: [:accept] do |id|
@text, @done = Devops::API2_0::Handler::Report.new(request, params).report(id)
erb :index
end
app.get "/status/:id" do
r = Devops::API2_0::Handler::Report.new(request, params).status(params[:id])
return [404, "Job with id '#{params[:id]}' not found"] if r.nil?
r
end
puts "Report routes initialized"
end
end
end
end
end
__END__
@@ layout
<html>
<head>
<% unless @done %>
<script>
function reload() {
location.reload();
}
setTimeout(reload, 5000);
</script>
<% end %>
</head>
<body>
<%= yield %>
</body>
</html>
@@ index
<pre>
<%= @text %>
</pre>

View File

@ -0,0 +1,115 @@
module Devops
module Version2_0
module Routes
module ScriptRoutes
def self.registered(app)
# Get scripts names
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* :
# [
# "script_1"
# ]
app.get_with_headers "/scripts", :headers => [:accept] do
check_privileges("script", "r")
json Devops::API2_0::Handler::Script.new(request, params).scripts
end
# Run command on node :node_name
#
# * *Request*
# - method : POST
# - body :
# command to run
#
# * *Returns* : text stream
app.post_with_statistic "/script/command/:node_name" do |node_name|
check_privileges("script", "x")
stream() do |out|
begin
Devops::API2_0::Handler::Script.new(request, params).execute_command(out, request.body.read, node_name)
rescue IOError => e
logger.error e.message
end
end
end
# Run script :script_name on nodes
#
# * *Request*
# - method : POST
# - headers :
# - Content-Type: application/json
# - body :
# {
# "nodes": [], -> array of nodes names
# "params": [] -> array of script arguments
# }
#
# * *Returns* : text stream
app.post_with_headers "/script/run/:script_name", :headers => [:content_type] do |script_name|
check_privileges("script", "x")
file_name = check_filename(script_name, "Parameter 'script_name' must be a not empty string", false)
body = create_object_from_json_body
nodes = check_array(body["nodes"], "Parameter 'nodes' must be a not empty array of strings")
p = check_array(body["params"], "Parameter 'params' should be a not empty array of strings", String, true)
stream() do |out|
begin
Devops::API2_0::Handler::Script.new(request, params).run_script out, file_name, nodes, p
rescue IOError => e
logger.error e.message
end
end
end
hash = {}
# Create script :script_name
#
# * *Request*
# - method : PUT
# - headers :
# - Accept: application/json
# - body : script content
#
# * *Returns* :
# 201 - Created
hash["PUT"] = lambda {
check_privileges("script", "w")
file_name = params[:script_name]
check_filename(file_name, "Parameter 'script_name' must be a not empty string")
Devops::API2_0::Handler::Script.new(request, params).create_script(file_name)
create_response("File '#{file_name}' created", nil, 201)
}
# Delete script :script_name
#
# * *Request*
# - method : Delete
# - headers :
# - Accept: application/json
#
# * *Returns* :
# 200 - Deleted
hash["DELETE"] = lambda {
check_privileges("script", "w")
file_name = params[:script_name]
check_filename(file_name, "Parameter 'script_name' must be a not empty string")
Devops::API2_0::Handler::Script.new(request, params).delete_script file_name
create_response("File '#{file_name}' deleted")
}
app.multi_routes "/script/:script_name", {:headers => [:accept]}, hash
puts "Script routes initialized"
end
end
end
end
end

View File

@ -1,7 +1,7 @@
require "json"
module Devops
module Version2_0
module API2_0
module Routes
module ServerRoutes
@ -23,7 +23,17 @@ module Devops
# "chef_node_name": "chef name"
# }
# ]
app.get_with_headers "/servers", :headers => [:accept], &Devops::Version2_0::Handler::Server.get_servers
app.get_with_headers "/servers", :headers => [:accept] do
check_privileges("server", "r")
fields = []
if params.key?("fields") and params["fields"].is_a?(Array)
Devops::Model::Server.fields.each do |k|
fields.push k if params["fields"].include?(k)
end
end
reserved = (params.key?("reserved") ? true : nil)
json Devops::API2_0::Handler::Server.new(request, params).servers(fields, reserved)
end
# Get chef nodes list
#
@ -38,7 +48,10 @@ module Devops
# "chef_node_name": "chef name"
# }
# ]
app.get_with_headers "/servers/chef", :headers => [:accept], &Devops::Version2_0::Handler::Server.get_chef_servers
app.get_with_headers "/servers/chef", :headers => [:accept] do
check_privileges("server", "r")
json Devops::API2_0::Handler::Server.new(request, params).chef_servers
end
# Get provider servers list
#
@ -76,7 +89,10 @@ module Devops
# "private_ip": "172.17.0.1"
# }
# ]
app.get_with_headers "/servers/:provider", :headers => [:accept], &Devops::Version2_0::Handler::Server.get_provider_servers
app.get_with_headers "/servers/:provider", :headers => [:accept] do
check_privileges("server", "r")
json Devops::API2_0::Handler::Server.new(request, params).provider_servers(params[:provider])
end
# Get server info by :name
#
@ -94,7 +110,10 @@ module Devops
# }
# ]
hash = {}
hash["GET"] = Devops::Version2_0::Handler::Server.get_server
hash["GET"] = lambda {|id|
check_privileges("server", "r")
json Devops::API2_0::Handler::Server.new(request, params).server(id).to_hash
}
# Delete devops server
#
@ -110,7 +129,13 @@ module Devops
#
# * *Returns* :
# 200 - Deleted
hash["DELETE"] = Devops::Version2_0::Handler::Server.delete_server
hash["DELETE"] = lambda {|id|
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
info, r = Devops::API2_0::Handler::Server.new(request, params).delete(id, key)
create_response(info, r)
}
app.multi_routes "/server/:id", {:headers => [:accept, :content_type]}, hash
# Create devops server
@ -133,7 +158,32 @@ module Devops
# }
#
# * *Returns* : text stream
app.post_with_headers "/server", :headers => [:content_type], &Devops::Version2_0::Handler::Server.create_server
app.post_with_headers "/server", :headers => [:content_type] do
check_privileges("server", "w")
body = create_object_from_json_body
project_name = check_string(body["project"], "Parameter 'project' must be a not empty string")
env_name = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string")
server_name = check_string(body["name"], "Parameter 'name' should be null or not empty string", true)
without_bootstrap = body["without_bootstrap"]
force = body["force"]
halt_response("Parameter 'without_bootstrap' should be a null or true") unless without_bootstrap.nil? or without_bootstrap == true
halt_response("Parameter 'force' should be a null or true") unless force.nil? or force == true
groups = check_array(body["groups"], "Parameter 'groups' should be null or not empty array of string", String, true)
key_name = check_string(body["key"], "Parameter 'key' should be null or not empty string", true)
handler = Devops::API2_0::Handler::Server.new(request, params)
if body.key?("trace")
stream() do |out|
begin
status = handler.create_server_stream out, body
out << create_status(status)
rescue IOError => e
logger.error e.message
end
end
else
json handler.create_server body
end
end
# Pause devops server by name
#
@ -149,7 +199,13 @@ module Devops
#
# * *Returns* :
# 200 - Paused
app.post_with_headers "/server/:node_name/pause", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.pause_server
app.post_with_headers "/server/:node_name/pause", :headers => [:accept, :content_type] do |node_name|
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
info = Devops::API2_0::Handler::Server.new(request, params).pause_server(node_name, key)
create_response(info)
end
# Unpause devops server by name
#
@ -165,7 +221,13 @@ module Devops
#
# * *Returns* :
# 200 - Unpaused
app.post_with_headers "/server/:node_name/unpause", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.unpause_server
app.post_with_headers "/server/:node_name/unpause", :headers => [:accept, :content_type] do |node_name|
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
info = Devops::API2_0::Handler::Server.new(request, params).unpause_server(node_name, key)
create_response(info)
end
# Reserve devops server
#
@ -181,7 +243,13 @@ module Devops
#
# * *Returns* :
# 200 - Reserved
app.post_with_headers "/server/:node_name/reserve", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.reserve_server
app.post_with_headers "/server/:node_name/reserve", :headers => [:accept, :content_type] do |node_name|
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
Devops::API2_0::Handler::Server.new(request, params).reserve_server(node_name, key)
create_response("Server '#{node_name}' has been reserved")
end
# Unreserve devops server
#
@ -197,7 +265,13 @@ module Devops
#
# * *Returns* :
# 200 - Unreserved
app.post_with_headers "/server/:node_name/unreserve", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.unreserve_server
app.post_with_headers "/server/:node_name/unreserve", :headers => [:accept, :content_type] do |node_name|
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
Devops::API2_0::Handler::Server.new(request, params).unreserve_server(node_name, key)
create_response("Server '#{node_name}' has been unreserved")
end
# Bootstrap devops server
#
@ -215,7 +289,32 @@ module Devops
# }
#
# * *Returns* : text stream
app.post_with_headers "/server/bootstrap", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.bootstrap_server
app.post_with_headers "/server/bootstrap", :headers => [:accept, :content_type] do
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
id = check_string(body["instance_id"], "Parameter 'instance_id' must be a not empty string")
name = check_string(body["name"], "Parameter 'name' should be a not empty string", true)
rl = check_array(body["run_list"], "Parameter 'run_list' should be a not empty array of string", String, true)
unless rl.nil?
validator = Validators::Helpers::RunList.new(rl)
halt_response(validator.message) unless validator.valid?
end
t = check_string(body["bootstrap_template"], "Parameter 'bootstrap_template' should be a not empty string", true)
handler = Devops::API2_0::Handler::Server.new(request, params)
if body.key?("trace")
stream() do |out|
begin
status = handler.bootstrap_server_stream out, body
out << create_status(status)
rescue IOError => e
logger.error e.message
end
end
else
handler.bootstrap_server(body)
end
end
# Add external server to devops
#
@ -236,7 +335,19 @@ module Devops
#
# * *Returns* :
# 200 - Added
app.post_with_headers "/server/add", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::Server.add_server
app.post_with_headers "/server/add", :headers => [:accept, :content_type] do
check_privileges("server", "w")
body = create_object_from_json_body
project = check_string(body["project"], "Parameter 'project' must be a not empty string")
deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string")
key = check_string(body["key"], "Parameter 'key' must be a not empty string")
remote_user = check_string(body["remote_user"], "Parameter 'remote_user' must be a not empty string")
private_ip = check_string(body["private_ip"], "Parameter 'private_ip' must be a not empty string")
public_ip = check_string(body["public_ip"], "Parameter 'public_ip' should be a not empty string", true)
info = Devops::API2_0::Handler::Server.new(request, params).add_server(body)
create_response(info)
end
puts "Server routes initialized"
end

View File

@ -0,0 +1,61 @@
module Devops
module API2_0
module Routes
module StackRoutes
def self.registered(app)
app.get_with_headers '/stacks', :headers => [:accept] do
check_privileges("stack", "r")
json Devops::API2_0::Handler::Stack.new(request, params).stacks.map(&:to_hash)
end
app.get_with_headers '/stacks/provider/:provider', :headers => [:accept] do |provider|
check_privileges("stack", "r")
check_provider(provider)
json Devops::API2_0::Handler::Stack.new(request, params).stacks_for_provider(provider).map(&:to_hash)
end
app.post_with_headers "/stack", :headers => [:accept] do
check_privileges("stack", "w")
object = create_object_from_json_body
m = Devops::API2_0::Handler::Stack.new(request, params).create_stack object
create_response "Created", m.to_hash, 201
end
hash = {}
hash['GET'] = lambda { |stack_id|
check_privileges("stack", "r")
json Devops::API2_0::Handler::Stack.new(request, params).stack(stack_id).to_hash
}
hash['DELETE'] = lambda { |stack_id|
check_privileges("stack", "w")
Devops::API2_0::Handler::Stack.new(request, params).delete_stack(stack_id)
create_response("Stack '#{stack_id}' has been removed")
}
app.multi_routes '/stack/:stack_id', {:headers => [:accept]}, hash
app.post_with_headers "/stack/:stack_id/sync_details", :headers => [:accept] do |stack_id|
check_privileges("stack", "w")
json Devops::API2_0::Handler::Stack.new(request, params).sync_details(stack_id).to_hash
end
app.get_with_headers "/stack/:stack_id/resources", :headers => [:accept] do |stack_id|
check_privileges("stack", "r")
json Devops::API2_0::Handler::Stack.new(request, params).resources(stack_id)
end
app.get_with_headers "/stack/:stack_id/resources/:resource_id", :headers => [:accept] do |stack_id, resource_id|
check_privileges("stack", "r")
json Devops::API2_0::Handler::Stack.new(request, params).resource(stack_id, resource_id)
end
puts "Stack routes initialized"
end
end
end
end
end

View File

@ -1,10 +1,10 @@
module Devops
module Version2_0
module API2_0
module Routes
module StackPresetRoutes
def self.registered(app)
# Get list of available stack_presets
# Get list of available stack_template_presets
#
# * *Request*
# - method : GET
@ -14,10 +14,12 @@ module Devops
# * *Returns* : array of hashes
# [ {id: 'preset id', template_preset_body: 'long body'} ]
#
app.get_with_headers "/stack_presets", :headers => [:accept], &Devops::Version2_0::Handler::StackPreset.get_presets
app.get_with_headers "/stack_presets", :headers => [:accept] do
# check_privileges("stack_template_presets", "r")
json Devops::API2_0::Handler::StackPreset.new(request, params).presets.map(&:to_hash)
end
# Get information about stack_preset
# Get information about stack_template_preset
#
# * *Request*
# - method : GET
@ -27,8 +29,10 @@ module Devops
# * *Returns* : hash
# {id: 'preset id', template_preset_body: 'long body'}
#
app.get_with_headers "/stack_presets/:id", :headers => [:accept], &Devops::Version2_0::Handler::StackPreset.get_preset
app.get_with_headers "/stack_presets/:id", :headers => [:accept] do |id|
# check_privileges("stack_template_presets", "r")
json Devops::API2_0::Handler::StackPreset.new(request, params).preset(id).to_hash
end
# Build stack template from preset
#
@ -38,7 +42,7 @@ module Devops
# - Accept: application/json
# - params :
# - provider: string
# - stack_id: id of stack template to create
# - stack_template_id: id of stack template to create
# - template_attrs: hash with template attributes
#
# TODO: not stack template, but stack itself
@ -50,7 +54,13 @@ module Devops
# template_body: 'long body'
# }
#
app.post_with_headers "/stack_presets/:id/apply", :headers => [:accept], &Devops::Version2_0::Handler::StackPreset.apply
app.post_with_headers "/stack_presets/:id/apply", :headers => [:accept] do |id|
# check_privileges("stack_template_presets", "r")
check_privileges('stack_template', 'w')
body = create_object_from_json_body
stack = Devops::API2_0::Handler::StackPreset.new(request, params).apply(id, body)
create_response 'Created', stack.to_hash, 201
end
puts "Stack template presets routes initialized"
end

View File

@ -0,0 +1,46 @@
module Devops
module API2_0
module Routes
module StackTemplateRoutes
def self.registered(app)
app.get_with_headers '/stack_templates', :headers => [:accept] do
check_privileges('stack_template', 'r')
json Devops::API2_0::Handler::StackTemplate.new(request, params).stack_templates.map(&:to_hash)
end
app.get_with_headers '/stack_templates/provider/:provider', :headers => [:accept] do |provider|
check_privileges('stack_template', 'r')
check_provider(provider)
json Devops::API2_0::Handler::StackTemplate.new(request, params).stack_templates_for_provider(provider).map(&:to_hash)
end
app.post_with_headers "/stack_template", :headers => [:accept] do
check_privileges('stack_template', 'w')
body = create_object_from_json_body
model = Devops::API2_0::Handler::StackTemplate.new(request, params).create_stack_template(body)
create_response 'Created', model.to_hash, 201
end
hash = {}
hash['GET'] = lambda {|stack_template_id|
check_privileges('stack_template', 'r')
json Devops::API2_0::Handler::StackTemplate.new(request, params).stack_template(stack_template_id).to_hash
}
hash['DELETE'] = lambda {|stack_template_id|
check_privileges('stack_template', 'w')
Devops::API2_0::Handler::StackTemplate.new(request, params).delete_stack_template(stack_template_id)
create_response("Template '#{stack_template_id}' has been removed")
}
app.multi_routes '/stack_template/:stack_template_id', {}, hash
puts "Stack_template routes initialized"
end
end
end
end
end

View File

@ -1,5 +1,5 @@
module Devops
module Version2_0
module API2_0
module Routes
module TagRoutes
@ -17,7 +17,10 @@ module Devops
# [
# "tag_1"
# ]
hash["GET"] = Devops::Version2_0::Handler::Tag.get_tags
hash["GET"] = lambda {
check_privileges("server", "r")
json Devops::API2_0::Handler::Tag.new(params[:node_name]).tags()
}
# Set tags list to :node_name
#
@ -33,7 +36,13 @@ module Devops
#
# * *Returns* :
# 200
hash["POST"] = Devops::Version2_0::Handler::Tag.set_tags
hash["POST"] = lambda {
check_privileges("server", "w")
tags = create_object_from_json_body(Array)
check_array(tags, "Request body should be a not empty array of strings")
Devops::Version2_0::Handler::Tag.new(params[:node_name]).set_tags(tags)
create_response("Set tags for #{params[:node_name]}", tags: tags)
}
# Delete tags from :node_name
#
@ -49,7 +58,13 @@ module Devops
#
# * *Returns* :
# 200
hash["DELETE"] = Devops::Version2_0::Handler::Tag.unset_tags
hash["DELETE"] = lambda {
check_privileges("server", "w")
tags = create_object_from_json_body(Array)
check_array(tags, "Request body should be a not empty array of strings")
Devops::Version2_0::Handler::Tag.new(params[:node_name]).unset_tags(tags)
create_response("Deleted tags for #{params[:node_name]}", tags: tags)
}
app.multi_routes "/tags/:node_name", {:headers => [:accept, :content_type]}, hash
puts "Tag routes initialized"

View File

@ -1,5 +1,5 @@
module Devops
module Version2_0
module API2_0
module Routes
module UserRoutes
@ -33,7 +33,11 @@ module Devops
# "id": "test"
# }
# ]
app.get_with_headers "/users", :headers => [:accept], &Devops::Version2_0::Handler::User.get_users
app.get_with_headers "/users", :headers => [:accept] do#, &Devops::API2_0::Handler::User.get_users
check_privileges("user", "r")
users = Devops::API2_0::Handler::User.new.users.map {|i| h = i.to_hash; h.delete("password"); h}
json users
end
# Create user
#
@ -51,7 +55,15 @@ module Devops
#
# * *Returns* :
# 201 - Created
app.post_with_headers "/user", :headers => [:accept, :content_type], &Devops::Version2_0::Handler::User.create_user
app.post_with_headers "/user", :headers => [:accept, :content_type] do#, &Devops::API2_0::Handler::User.create_user
check_privileges("user", "w")
user = create_object_from_json_body
["username", "password", "email"].each do |p|
check_string(user[p], "Parameter '#{p}' must be a not empty string")
end
Devops::API2_0::Handler::User.new.create(user)
create_response("Created", nil, 201)
end
hash = {}
# Delete user
@ -63,7 +75,24 @@ module Devops
#
# * *Returns* :
# 200 - Deleted
hash["DELETE"] = Devops::Version2_0::Handler::User.delete_user
hash["DELETE"] = lambda {
check_privileges("user", "w")
projects = Devops::Db.connector.projects_by_user params[:user]
if !projects.empty?
str = ""
projects.each do |p|
p.deploy_envs.each do |e|
str+="#{p.id}.#{e.identifier} " if e.users.include? params[:user]
end
end
logger.info projects
raise DependencyError.new "Deleting is forbidden: User is included in #{str}"
#return [400, "Deleting is forbidden: User is included in #{str}"]
end
Devops::API2_0::Handler::User.new.delete(params[:user])
create_response("User '#{params[:user]}' removed")
}
# Change user privileges
#
@ -80,7 +109,14 @@ module Devops
#
# * *Returns* :
# 200 - Updated
hash["PUT"] = Devops::Version2_0::Handler::User.change_user_privileges
hash["PUT"] = lambda {
check_privileges("user", "w")
data = create_object_from_json_body
cmd = check_string(data["cmd"], "Parameter 'cmd' should be a not empty string", true) || ""
privileges = check_string(data["privileges"], "Parameter 'privileges' should be a not empty string", true) || ""
Devops::API2_0::Handler::User.new.change_user_privileges(params[:user], cmd, privileges)
create_response("Updated")
}
app.multi_routes "/user/:user", {:headers => [:accept, :content_type]}, hash
# Change user email/password
@ -97,7 +133,20 @@ module Devops
#
# * *Returns* :
# 200 - Updated
app.put_with_headers %r{\A/user/#{DevopsConfig::OBJECT_NAME}/(email|password)\z}, :headers => [:accept, :content_type], &Devops::Version2_0::Handler::User.change_user_email_or_password
app.put_with_headers %r{\A/user/#{DevopsConfig::OBJECT_NAME}/(email|password)\z}, :headers => [:accept, :content_type] do#, &Devops::API2_0::Handler::User.change_user_email_or_password
check_privileges("user", "w")
action = File.basename(request.path)
u = File.basename(File.dirname(request.path))
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
body = create_object_from_json_body
p = check_string(body[action], "Parameter '#{action}' must be a not empty string")
h = Devops::API2_0::Handler::User.new
h.send("change_#{action}=", p)
create_response("Updated")
end
puts "User routes initialized"
end

View File

@ -1,20 +1,32 @@
require "sinatra/base"
require "sinatra/streaming"
require "helpers/version_2"
require "json"
require "fog"
require "auth/devops_auth"
require "exceptions/invalid_record"
require "exceptions/record_not_found"
require "exceptions/dependency_error"
require "exceptions/conflict_exception"
require 'core/devops-logger'
require_relative "../helpers/version_2"
module Devops
class Api2 < Sinatra::Base
include Sinatra::JSON
helpers Sinatra::Streaming
helpers Devops::Version2_0::Helpers
helpers Devops::API2_0::Helpers
register Sinatra::DevopsAuth
configure :production do
config = DevopsConfig.config
log_file = File.join(config[:log_dir], "devops-api2.log")
logger = DevopsLogger.create(log_file, Logger::DEBUG)
use Rack::CommonLogger, logger
disable :dump_errors
disable :show_exceptions
set :logging, Logger::INFO
@ -22,11 +34,14 @@ module Devops
end
configure :development do
set :logging, Logger::DEBUG
config = DevopsConfig.config
log_file = File.join(config[:log_dir], "devops-api2.log")
logger = DevopsLogger.create(log_file, Logger::DEBUG)
use Rack::CommonLogger, logger
disable :raise_errors
# disable :dump_errors
set :show_exceptions, :after_handler
puts "Development mode"
logger.info "Development mode"
end
not_found do
@ -58,6 +73,12 @@ module Devops
halt_response(e.message, 400)
end
error ConflictException do
e = env["sinatra.error"]
logger.warn e.message
halt_response(e.message, 409)
end
error InvalidPrivileges do
e = env["sinatra.error"]
logger.warn e.message

View File

@ -1,4 +1,6 @@
require 'sinatra/base'
require 'core/devops-logger'
require 'core/devops-config'
class Client < Sinatra::Base
@ -7,6 +9,29 @@ class Client < Sinatra::Base
@@config = DevopsConfig.config
end
configure :production do
config = DevopsConfig.config
log_file = File.join(config[:log_dir], "devops-client.log")
logger = DevopsLogger.create(log_file, Logger::INFO)
use Rack::CommonLogger, logger
disable :dump_errors
disable :show_exceptions
# set :logging, Logger::INFO
logger.info "Production mode"
end
configure :development do
config = DevopsConfig.config
log_file = File.join(config[:log_dir], "devops-client.log")
logger = DevopsLogger.create(log_file, Logger::DEBUG)
use Rack::CommonLogger, logger
# set :logging, Logger::DEBUG
disable :raise_errors
# disable :dump_errors
set :show_exceptions, :after_handler
logger.info "Development mode"
end
# Route to download devops client
get "/devops-client.gem" do
begin

View File

@ -1,81 +1,78 @@
module Devops
module Application
class DevopsApi2Application < Application
class DevopsApi2Application < Application
def prepare
require "routes/v2.0"
require "routes/v2.0/handlers/provider"
require "routes/v2.0/handlers/bootstrap_templates"
require "routes/v2.0/handlers/deploy"
require "routes/v2.0/handlers/filter"
require "routes/v2.0/handlers/flavor"
require "routes/v2.0/handlers/group"
require "routes/v2.0/handlers/image"
require "routes/v2.0/handlers/network"
require "routes/v2.0/handlers/key"
require "routes/v2.0/handlers/project"
require "routes/v2.0/handlers/script"
require "routes/v2.0/handlers/status"
require "routes/v2.0/handlers/tag"
require "routes/v2.0/handlers/user"
require "routes/v2.0/handlers/server"
require "routes/v2.0/handlers/stack"
require "routes/v2.0/handlers/stack_template"
require "routes/v2.0/stack_presets"
require "routes/v2.0/handlers/report"
def prepare
require_relative "api2/routes/v2.0"
require_relative "api2/handlers/provider"
require_relative "api2/handlers/flavor"
require_relative "api2/handlers/filter"
require_relative "api2/handlers/group"
require_relative "api2/handlers/user"
require_relative "api2/handlers/network"
require_relative "api2/handlers/report"
require_relative "api2/handlers/deploy"
require_relative "api2/handlers/script"
require_relative "api2/handlers/bootstrap_templates"
require_relative "api2/handlers/key"
require_relative "api2/handlers/tag"
require_relative "api2/handlers/server"
require_relative "api2/handlers/image"
require_relative "api2/handlers/project"
require 'lib/stubber'
end
def init
config = DevopsConfig.config
Devops::Api2.set :devops_home, config[:devops_dir]
#set :config, config
Devops::Api2.set :keys_dir, (config[:keys_dir] || File.join(config[:devops_dir], "files/keys"))
Devops::Api2.set :scripts_dir, (config[:scripts_dir] || File.join(config[:devops_dir], "files/scripts"))
[:keys_dir, :scripts_dir].each {|key| d = Devops::Api2.settings.send(key); FileUtils.mkdir_p(d) unless File.exists?(d) }
init_mongo
Devops::Api2.settings.mongo.create_root_user
::Provider::ProviderFactory.init(config)
Stubber.stub_providers!(config[:stub_providers])
end
def routes
require "routes/v2.0/flavor"
require "routes/v2.0/image"
require "routes/v2.0/filter"
require "routes/v2.0/network"
require "routes/v2.0/group"
require "routes/v2.0/deploy"
require "routes/v2.0/project"
require "routes/v2.0/key"
require "routes/v2.0/user"
require "routes/v2.0/provider"
require "routes/v2.0/tag"
require "routes/v2.0/server"
require "routes/v2.0/script"
require "routes/v2.0/status"
require "routes/v2.0/bootstrap_templates"
require "routes/v2.0/stack"
require "routes/v2.0/stack_template"
require "routes/v2.0/handlers/stack_preset"
require "routes/v2.0/report"
routes = Devops::Version2_0::Routes.constants.collect{|s| Devops::Version2_0::Routes.const_get(s)}.select {|const| const.class == Module}
routes.each do |r|
Devops::Api2.register r
end
Routes.route "/v2.0", Devops::Api2
end
private
def init_mongo
Devops::Api2.set :mongo, Devops::Db.connector
end
require_relative "api2/handlers/stack"
require_relative "api2/handlers/stack_template"
require_relative "api2/handlers/stack_preset"
require 'lib/stubber'
end
def init
config = DevopsConfig.config
Devops::Api2.set :devops_home, config[:devops_dir]
#set :config, config
Devops::Api2.set :keys_dir, (config[:keys_dir] || File.join(config[:devops_dir], "files/keys"))
Devops::Api2.set :scripts_dir, (config[:scripts_dir] || File.join(config[:devops_dir], "files/scripts"))
[:keys_dir, :scripts_dir].each {|key| d = Devops::Api2.settings.send(key); FileUtils.mkdir_p(d) unless File.exists?(d) }
init_mongo
Devops::Api2.settings.mongo.create_root_user
::Provider::ProviderFactory.init(config)
Stubber.stub_providers! if config[:stub_classes]
end
def routes
require_relative "api2/routes/flavor"
require_relative "api2/routes/image"
require_relative "api2/routes/filter"
require_relative "api2/routes/network"
require_relative "api2/routes/group"
require_relative "api2/routes/deploy"
require_relative "api2/routes/project"
require_relative "api2/routes/key"
require_relative "api2/routes/user"
require_relative "api2/routes/provider"
require_relative "api2/routes/tag"
require_relative "api2/routes/server"
require_relative "api2/routes/script"
require_relative "api2/routes/bootstrap_templates"
require_relative "api2/routes/stack"
require_relative "api2/routes/stack_template"
require_relative "api2/routes/stack_presets"
require_relative "api2/routes/report"
routes = Devops::API2_0::Routes.constants.collect{|s| Devops::API2_0::Routes.const_get(s)}.select {|const| const.class == Module}
routes.each do |r|
Devops::Api2.register r
end
Routes.route "/v2.0", Devops::Api2
end
private
def init_mongo
Devops::Api2.set :mongo, Devops::Db.connector
end
end
end

View File

@ -1,28 +0,0 @@
module Devops
module Application
class Application
@@applications = []
def self.inherited(base)
@@applications << base.new
puts "Devops application '#{base}' has been added"
end
def self.applications
@@applications
end
def prepare
end
def init
end
def routes
end
end
end
end

View File

@ -1,14 +1,12 @@
module Devops
module Application
class DevopsClientApplication < Application
class DevopsClientApplication < Application
def prepare
require "app/client/devops-client"
end
def prepare
require "app/client/devops-client"
end
def routes
Routes.route "/client", ::Client.new
end
def routes
Routes.route "/client", ::Client.new
end
end
end

View File

@ -1,14 +1,12 @@
module Devops
module Application
class DevopsVersionApplication < Application
class DevopsVersionApplication < Application
def prepare
require "app/version/version"
end
def prepare
require "app/version/version"
end
def routes
Routes.route "/version", ::DevopsVersion.new
end
def routes
Routes.route "/version", ::DevopsVersion.new
end
end
end

View File

@ -1,17 +1,22 @@
require 'core/devops-logger'
require 'core/devops-config'
module Devops
module Application
class SidekiqApplication < Application
def prepare
require "sidekiq/web"
end
def routes
Devops::Routes.route "/sidekiq", Sidekiq::Web
end
class SidekiqApplication < Application
def prepare
require "sidekiq/web"
end
def routes
config = DevopsConfig.config
log_file = File.join(config[:log_dir], "devops-sidekiq-web.log")
logger = DevopsLogger.create(log_file, Logger::INFO)
Sidekiq::Web.use Rack::CommonLogger, logger
Devops::Routes.route "/sidekiq", Sidekiq::Web
end
end
end

View File

@ -2,7 +2,7 @@ require 'sinatra/base'
class DevopsVersion < Sinatra::Base
VERSION = "3.0.0"
VERSION = "2.3.0"
get "/" do
VERSION

View File

@ -0,0 +1,4 @@
require "app/sidekiq_web"
require "app/devops-client"
require "app/devops-version"
require "app/devops-api2"

View File

@ -12,8 +12,9 @@ module Sinatra
if @auth.provided? and @auth.basic? and @auth.credentials
c = @auth.credentials
begin
Devops::Db.connector.user_auth(c[0], c[1])
u = Devops::Db.connector.user_auth(c[0], c[1])
request.env['REMOTE_USER'] = c[0]
request.env['USER'] = u
true
rescue RecordNotFound => e
false

View File

@ -2,7 +2,8 @@ module BootstrapTemplatesCommands
def get_templates
res = []
Dir.foreach("#{ENV["HOME"]}/.chef/bootstrap/") {|f| res.push(f[0..-5]) if f.end_with?(".erb")} if File.exists? "#{ENV["HOME"]}/.chef/bootstrap/"
dir = "#{ENV["HOME"]}/.chef/bootstrap/"
Dir.foreach(dir) {|f| res.push(f[0..-5]) if f.end_with?(".erb")} if File.exists? dir
res
end

View File

@ -1,4 +1,3 @@
require "db/exceptions/invalid_record"
require "commands/image"
module DeployEnvCommands

View File

@ -1,6 +1,6 @@
require "commands/knife_commands"
require "commands/deploy"
require "db/exceptions/record_not_found"
require "exceptions/record_not_found"
module ServerCommands

View File

@ -1,5 +1,5 @@
# path to log file
config[:log_file] = "/path/to/log"
# path to log dir
config[:log_dir] = "/path/to/log"
# path to chef knife.rb file
config[:knife_config_file] = "/path/to/.chef/knife.rb"
# role name separator
@ -43,4 +43,4 @@ config[:debug] = true
# set it to :all or [:ec2] to stub calls to selected providers
# or to false to disable stubbing
config[:stub_providers] = false
config[:stub_providers] = false

View File

@ -4,11 +4,12 @@ require "rubygems"
require "bundler/setup"
require 'byebug'
require_relative "devops-service"
require_relative "devops_config"
require_relative "devops-routes"
root = File.dirname(__FILE__)
$:.push root
require_relative "core/devops-service"
require_relative "core/devops-config"
require_relative "core/devops-routes"
# Read configuration file
DevopsConfig.read
@ -27,6 +28,10 @@ config[:report_dir_v2] = File.expand_path(File.join(config[:devops_dir], "report
DevopsService.init
puts Devops::Routes.routes
if Devops::Routes.routes.empty?
puts "No applications"
exit -1
end
Devops::Routes.routes.each do |p, c|
map(p) do
run c

View File

@ -0,0 +1,26 @@
module Devops
class Application
@@applications = []
def self.inherited(base)
@@applications << base.new
puts "Devops application '#{base}' has been added"
end
def self.applications
@@applications
end
def prepare
end
def init
end
def routes
end
end
end

View File

@ -9,7 +9,7 @@ class DevopsConfig
class << self
def read config_file=nil
if config_file.nil?
config_file = ENV['DEVOPS_CONFIG'] || ENV['CONFIG'] || File.join(File.dirname(__FILE__), 'config.rb')
config_file = ENV['DEVOPS_CONFIG'] || ENV['CONFIG'] || File.join(File.dirname(__FILE__), '../config.rb')
end
config = {:url_prefix => ""}
if File.exists? config_file

View File

@ -1,3 +1,5 @@
require "db/mongo/mongo_connector"
module Devops
class Db

View File

@ -0,0 +1,16 @@
require "logger"
class DevopsLogger
def self.create out, level=Logger::INFO, format='%a %d-%m-%Y %H%M '
@_logger = Logger.new out
@_logger.level = level
@_logger.datetime_format = format
@_logger.debug("Logger has been created")
@_logger
end
def self.logger
@_logger
end
end

View File

@ -1,29 +1,29 @@
require "wisper"
$:.push File.dirname(__FILE__)
=begin
require "db/exceptions/invalid_record"
require "db/exceptions/record_not_found"
require "exceptions/dependency_error"
require "db/validators/all"
require "db/mongo/mongo_connector"
require "providers/provider_factory"
require "fog"
require "loader"
require "devops_db"
require "devops_logger"
require_relative "routes/v2.0"
require "hooks"
require "app/devops-application"
require "app/devops-client"
require "app/sidekiq_web"
require "app/devops-version"
require "app/devops-api2"
=end
require_relative "sinatra/methods_with_headers"
require_relative "devops-loader"
require_relative "devops-db"
require_relative "devops-logger"
require_relative "devops-application"
require_relative "../sinatra/methods_with_headers"
require_relative "../applications"
#root = File.expand_path("../", __FILE__)
#$:.push root
class DevopsService
@ -51,7 +51,7 @@ class DevopsService
DevopsLogger.create STDOUT
routes
apps = Devops::Application::Application.applications
apps = Devops::Application.applications
apps.each do |a|
a.prepare
end
@ -91,4 +91,4 @@ class DevopsService
end
require "wisper_fix"
require_relative "../wisper_fix"

View File

@ -1,5 +1,5 @@
require "db/exceptions/record_not_found"
require "db/exceptions/invalid_record"
require "exceptions/record_not_found"
require "exceptions/invalid_record"
require "exceptions/invalid_command"
require "exceptions/invalid_privileges"

View File

@ -1,3 +1,4 @@
require "db/mongo/models/user"
module Connectors
class User < Base
include Helpers::InsertCommand,
@ -13,6 +14,7 @@ module Connectors
def user_auth user, password
u = collection.find('_id' => user, 'password' => password).to_a.first
raise RecordNotFound.new('Invalid username or password') if u.nil?
model_from_bson(u)
end
def users(ids=nil)
@ -33,19 +35,6 @@ module Connectors
collection.insert(root.to_mongo_hash)
end
def check_user_privileges(id, cmd, required_privelege)
user = show(id)
unless %w(r w x).include?(required_privelege)
raise InvalidPrivileges.new("Access internal problem with privilege '#{required_privelege}'")
end
unless user.can?(cmd, required_privelege)
raise InvalidPrivileges.new("Access denied for '#{user.id}'")
end
true
end
private
def model_from_bson(bson)

View File

@ -1,5 +1,4 @@
require "db/mongo/models/mongo_model"
require "db/exceptions/invalid_record"
require "providers/provider_factory"
require "commands/deploy_env"

View File

@ -1,4 +1,11 @@
require "db/mongo/models/deploy_env/deploy_env_base"
require "db/validators/deploy_env/run_list"
require "db/validators/deploy_env/expiration"
require "db/validators/deploy_env/users"
require "db/validators/deploy_env/flavor"
require "db/validators/deploy_env/image"
require "db/validators/deploy_env/subnet_belongs_to_provider"
require "db/validators/deploy_env/groups"
module Devops
module Model

View File

@ -1,5 +1,4 @@
require "db/mongo/models/mongo_model"
require "db/exceptions/invalid_record"
require "commands/deploy_env"
module Devops

View File

@ -1,5 +1,13 @@
require "db/mongo/models/deploy_env/deploy_env_base"
require "providers/provider_factory"
require "db/validators/deploy_env/run_list"
require "db/validators/deploy_env/expiration"
require "db/validators/deploy_env/users"
require "db/validators/deploy_env/flavor"
require "db/validators/deploy_env/image"
require "db/validators/deploy_env/subnet_not_empty"
require "db/validators/deploy_env/subnet_belongs_to_provider"
require "db/validators/deploy_env/groups"
module Devops
module Model

View File

@ -1,4 +1,7 @@
require "db/mongo/models/deploy_env/deploy_env_base"
require "db/validators/deploy_env/run_list"
require "db/validators/deploy_env/expiration"
require "db/validators/deploy_env/users"
module Devops
module Model

View File

@ -1,5 +1,6 @@
require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model"
require "db/validators/image/bootstrap_template"
require "db/validators/image/image_in_filter"
module Devops
module Model

View File

@ -1,6 +1,7 @@
require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model"
require "json"
require "db/validators/key/file_existence"
require "db/validators/key/scope"
module Devops
module Model

View File

@ -1,5 +1,5 @@
require "providers/provider_factory"
require "db/exceptions/invalid_record"
require "exceptions/invalid_record"
require "json"
require 'db/validators/all'

View File

@ -1,10 +1,9 @@
require "db/exceptions/invalid_record"
require "db/exceptions/record_not_found"
require "db/mongo/models/deploy_env/deploy_env_factory"
require "db/mongo/models/user"
require "db/mongo/models/deploy_env/deploy_env_multi"
require "db/mongo/models/mongo_model"
require "json"
require "hooks"
require "lib/project/handler/types_factory"
module Devops

View File

@ -1,4 +1,3 @@
require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model"
module Devops

View File

@ -1,4 +1,3 @@
require "db/exceptions/invalid_record"
require "db/mongo/models/mongo_model"
module Devops

View File

@ -1,4 +1,4 @@
require "db/exceptions/invalid_record"
require "exceptions/invalid_record"
require "exceptions/invalid_command"
require "db/mongo/models/mongo_model"
@ -70,15 +70,11 @@ module Devops
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)
def check_privileges cmd, required_privelege
unless PRIVILEGES.include?(required_privelege)
raise InvalidPrivileges.new("Access internal problem with privilege '#{required_privelege}'")
end
can?(cmd, required_privelege)
end
def self.create_root
@ -89,6 +85,12 @@ module Devops
end
private
def can?(command, privilege)
p = self.privileges[command] || []
p.include?(privilege)
end
def privileges_with_value value, options={}
privileges = {}
[

View File

@ -1,40 +1,42 @@
class Validators::Base
module Validators
class Base
def initialize(model, options={})
@model = model
@options = options
end
def initialize(model, options={})
@model = model
@options = options
end
def validate!
raise InvalidRecord.new(message) unless valid?
end
def validate!
raise InvalidRecord.new(message) unless valid?
end
def valid?
raise 'override me'
end
def valid?
raise 'override me'
end
def message
raise 'override me'
end
def message
raise 'override me'
end
class << self
private
class << self
private
# this method delegates @valid? and @message methods to helper validator, passed as block
def delegate_to_helper_validator(&block)
# this method delegates @valid? and @message methods to helper validator, passed as block
def delegate_to_helper_validator(&block)
define_method :helper_validator do
@helper_validator ||= self.instance_eval(&block)
end
define_method :helper_validator do
@helper_validator ||= self.instance_eval(&block)
end
define_method :valid? do
self.helper_validator.valid?
end
define_method :valid? do
self.helper_validator.valid?
end
define_method :message do
self.helper_validator.message
define_method :message do
self.helper_validator.message
end
end
end
end
end
end

View File

@ -2,8 +2,10 @@
# not only in deploy env model
module Validators
class DeployEnv::RunList < Base
module DeployEnv
class RunList < Base
delegate_to_helper_validator { Helpers::RunList.new(@model.run_list) }
delegate_to_helper_validator { Helpers::RunList.new(@model.run_list) }
end
end
end

View File

@ -1,20 +1,24 @@
require "commands/bootstrap_templates"
require "db/validators/base"
module Validators
class Image::BootstrapTemplate < Base
module Image
class BootstrapTemplate < Base
include BootstrapTemplatesCommands
include BootstrapTemplatesCommands
def valid?
if @model.bootstrap_template
templates = get_templates
templates.include?(@model.bootstrap_template)
else
true
def valid?
if @model.bootstrap_template
templates = get_templates
templates.include?(@model.bootstrap_template)
else
true
end
end
end
def message
"Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'"
def message
"Invalid bootstrap template '#{@model.bootstrap_template}' for image '#{@model.id}'"
end
end
end
end

View File

@ -1,7 +1,9 @@
module Validators
class Key::FileExistence < Base
module Key
class FileExistence < Base
delegate_to_helper_validator { Helpers::FileExistence.new(@model.path) }
delegate_to_helper_validator { Helpers::FileExistence.new(@model.path) }
end
end
end

View File

@ -1,16 +0,0 @@
require "logger"
class DevopsLogger
def self.create out, level=Logger::INFO
@_logger = Logger.new out
@_logger.level = level
@_logger.datetime_format = '%a %d-%m-%Y %H%M '
@_logger.info("Logger has been created")
@_logger
end
def self.logger
@_logger
end
end

View File

@ -0,0 +1,4 @@
class ConflictException < Exception#StandardError
end

View File

@ -1,31 +0,0 @@
module Devops
module Version2_0
module Routes
module DeployRoutes
def self.registered(app)
# Run chef-client on reserved server
#
# * *Request*
# - method : POST
# - headers :
# - Content-Type: application/json
# - body :
# {
# "names": [], -> array of servers names to run chef-client
# "tags": [], -> array of tags to apply on each server before running chef-client
# "trace": true -> return output in stream
# }
#
# * *Returns* : text stream
app.post_with_headers "/deploy", :headers => [:content_type], &Devops::Version2_0::Handler::Deploy.deploy
puts "Deploy routes initialized"
end
end
end
end
end

View File

@ -1,20 +0,0 @@
require "commands/bootstrap_templates"
module Devops
module Version2_0
module Handler
class BootstrapTemplates
extend BootstrapTemplatesCommands
def self.get_bootstrap_templates
lambda {
check_privileges("templates", "r")
# broadcast(:cancel_order_failed, "hello")
json BootstrapTemplates.get_templates
}
end
end
end
end
end

View File

@ -1,70 +0,0 @@
require "commands/deploy"
require "commands/status"
require "workers/deploy_worker"
module Devops
module Version2_0
module Handler
class Deploy
extend DeployCommands
extend StatusCommands
def self.deploy
lambda {
check_privileges("server", "x")
# TODO: send message
#broadcast(:devops_deploy, "deploy")
r = create_object_from_json_body
names = check_array(r["names"], "Parameter 'names' should be a not empty array of strings")
tags = check_array(r["tags"], "Parameter 'tags' should be an array of strings", String, true) || []
servers = settings.mongo.servers(nil, nil, names, true)
halt(404, "No reserved servers found for names '#{names.join("', '")}'") if servers.empty?
keys = {}
servers.sort_by!{|s| names.index(s.chef_node_name)}
if r.key?("trace")
stream() do |out|
status = []
begin
servers.each do |s|
project = begin
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
out << e.message + "\n"
status.push 2
next
end
res = deploy_server_proc.call(out, s, settings.mongo, tags)
status.push(res)
end
out << create_status(status)
rescue IOError => e
logger.error e.message
break
end
end # stream
else
dir = DevopsService.config[:report_dir_v2]
files = []
uri = URI.parse(request.url)
servers.each do |s|
project = begin
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
next
end
jid = DeployWorker.perform_async(dir, s.to_hash, tags, request.env['REMOTE_USER'], DevopsService.config)
logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsService.config[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s
end
sleep 1
json files
end
}
end
end
end
end
end

View File

@ -1,30 +0,0 @@
module Devops
module Version2_0
module Handler
class Filter
def self.get_filters
lambda {
check_privileges("filter", "r")
check_provider(params[:provider])
json settings.mongo.available_images(params[:provider])
}
end
def self.add_filter
lambda {
create_response("Updated", {:images => settings.mongo.add_available_images(@images, params[:provider])})
}
end
def self.delete_filter
lambda {
create_response("Deleted", {:images => settings.mongo.delete_available_images(@images, params[:provider])})
}
end
end
end
end
end

View File

@ -1,19 +0,0 @@
require "providers/provider_factory"
module Devops
module Version2_0
module Handler
class Flavor
def self.get_flavors
lambda {
check_privileges("flavor", "r")
check_provider(params[:provider])
p = ::Provider::ProviderFactory.get params[:provider]
json p.flavors
}
end
end
end
end
end

View File

@ -1,19 +0,0 @@
require "providers/provider_factory"
module Devops
module Version2_0
module Handler
class Group
def self.get_groups
lambda {
check_privileges("group", "r")
check_provider(params[:provider])
p = ::Provider::ProviderFactory.get params[:provider]
json p.groups(params)
}
end
end
end
end
end

View File

@ -1,75 +0,0 @@
require "providers/provider_factory"
require "commands/image"
module Devops
module Version2_0
module Handler
class Image
extend ImageCommands
def self.get_images
lambda {
check_privileges("image", "r")
check_provider(params[:provider]) if params[:provider]
images = settings.mongo.images(params[:provider])
json(images.map {|i| i.to_hash})
}
end
def self.get_provider_images
lambda {
check_privileges("image", "r")
check_provider(params[:provider])
json Image.get_available_provider_images(settings.mongo, params[:provider])
}
end
def self.get_image
lambda {
check_privileges("image", "r")
json settings.mongo.image(params[:image_id])
}
end
def self.create_image
lambda {
check_privileges("image", "w")
image = create_object_from_json_body
settings.mongo.image_insert Devops::Model::Image.new(image)
create_response "Created", nil, 201
}
end
def self.update_image
lambda {
check_privileges("image", "w")
settings.mongo.image params[:image_id]
image = Devops::Model::Image.new(create_object_from_json_body)
image.id = params[:image_id]
settings.mongo.image_update image
create_response("Image '#{params[:image_id]}' has been updated")
}
end
def self.delete_image
lambda {
check_privileges("image", "w")
projects = settings.mongo.projects_by_image params[:image_id]
unless projects.empty?
ar = []
projects.each do |p|
ar += p.deploy_envs.select{|e| e.respond_to?(:image)}.select{|e| e.image == params[:image_id]}.map{|e| "#{p.id}.#{e.identifier}"}
end
raise DependencyError.new "Deleting is forbidden: Image is used in #{ar.join(", ")}"
end
r = settings.mongo.image_delete params[:image_id]
create_response("Image '#{params[:image_id]}' has been removed")
}
end
end
end
end
end

View File

@ -1,60 +0,0 @@
module Devops
module Version2_0
module Handler
class Key
def self.get_keys
lambda {
check_privileges("key", "r")
keys = settings.mongo.keys.map {|i| i.to_hash}
keys.each {|k| k.delete("path")} # We should not return path to the key
json keys
}
end
def self.create_key
lambda {
check_privileges("key", "w")
key = create_object_from_json_body
fname = check_filename(key["file_name"], "Parameter 'file_name' must be a not empty string")
kname = check_string(key["key_name"], "Parameter 'key_name' should be a not empty string")
content = check_string(key["content"], "Parameter 'content' should be a not empty string")
file_name = File.join(settings.keys_dir, fname)
halt(400, "File '#{fname}' already exist") if File.exists?(file_name)
File.open(file_name, "w") do |f|
f.write(content)
f.chmod(0400)
end
key = Devops::Model::Key.new({"path" => file_name, "id" => kname})
settings.mongo.key_insert key
create_response("Created", nil, 201)
}
end
def self.delete_key
lambda {
check_privileges("key", "w")
servers = settings.mongo.servers_by_key params[:key]
unless servers.empty?
s_str = servers.map{|s| s.id}.join(", ")
raise DependencyError.new "Deleting is forbidden: Key is used in servers: #{s_str}"
end
k = settings.mongo.key params[:key]
begin
FileUtils.rm(k.path)
rescue
logger.error "Missing key file for #{params[:key]} - #{k.filename}"
end
r = settings.mongo.key_delete params[:key]
return [500, r["err"].inspect] unless r["err"].nil?
create_response("Key '#{params[:key]}' removed")
}
end
end
end
end
end

View File

@ -1,20 +0,0 @@
require "providers/provider_factory"
module Devops
module Version2_0
module Handler
class Network
def self.get_networks
lambda {
check_privileges("network", "r")
check_provider(params[:provider])
p = ::Provider::ProviderFactory.get params[:provider]
json p.networks_detail
}
end
end
end
end
end

View File

@ -1,315 +0,0 @@
require "commands/deploy"
require "commands/status"
require "commands/server"
require "db/mongo/models/project"
require "workers/project_test_worker"
module Devops
module Version2_0
module Handler
class Project
extend DeployCommands
extend StatusCommands
extend ServerCommands
def self.get_projects
lambda {
check_privileges("project", "r")
fields = []
if params.key?("fields") and params["fields"].is_a?(Array)
Devops::Model::Project.fields.each do |k|
fields.push k if params["fields"].include?(k)
end
end
archived = params.include?("archived")
json settings.mongo.projects(nil, nil, fields, archived).map {|p| p.to_hash}
}
end
def self.get_project
lambda {
check_privileges("project", "r")
json settings.mongo.project(params[:project])
}
end
def self.get_project_servers
lambda {
check_privileges("project", "r")
settings.mongo.project(params[:project])
json settings.mongo.servers(params[:project], params[:deploy_env]).map{|s| s.to_hash}
}
end
def self.get_project_stacks
lambda {
check_privileges("project", "r")
settings.mongo.project(params[:project])
options = {project: params[:project]}
options[:deploy_env] = params[:deploy_env] if params[:deploy_env]
json settings.mongo.stacks(options).map{|s| s.to_hash}
}
end
# TODO: multi project
def self.create_project
lambda {
check_privileges("project", "w")
body = create_object_from_json_body
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)
p = Devops::Model::Project.new(body)
halt_response("Project '#{p.id}' already exist") if settings.mongo.is_project_exists?(p)
p.add_authorized_user [request.env['REMOTE_USER']]
p.create
roles_res = ""
if p.multi?
logger.info "Project '#{p.id}' with type 'multi' created"
else
logger.info "Project '#{p.id}' created"
roles = Project.create_roles p.id, p.deploy_envs, logger
roles_res = ". " + Project.create_roles_response(roles)
end
res = "Created" + roles_res
create_response(res, nil, 201)
}
end
# TODO: multi project
def self.update_project
lambda {
check_privileges("project", "w")
project = Devops::Model::Project.new(create_object_from_json_body)
project.id = params[:project]
old_project = settings.mongo.project params[:project]
settings.mongo.project_update project
roles = Devops::Version2_0::Handler::Project.create_new_roles(old_project, project, logger)
info = "Project '#{project.id}' has been updated." + Project.create_roles_response(roles)
create_response(info)
}
end
# TODO: multi project
def self.update_project_users
lambda {
check_privileges("project", "w")
body = create_object_from_json_body
users = check_array(body["users"], "Parameter 'users' must be a not empty array of strings")
deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string", true)
project = settings.mongo.project(params[:id])
users = settings.mongo.users(users).map{|u| u.id}
buf = users - users
project.add_authorized_user users, deploy_env
settings.mongo.project_update(project)
info = "Users '#{users.join("', '")}' have been added to '#{params[:id]}' project's authorized users"
info << ", invalid users: '#{buf.join("', '")}'" unless buf.empty?
create_response(info)
}
end
# TODO: multi project
def self.delete_project_users
lambda {
check_privileges("project", "w")
@project.remove_authorized_user @users, @deploy_env
settings.mongo.project_update @project
info = "Users '#{@users.join("', '")}' have been removed from '#{params[:id]}' project's authorized users"
create_response(info)
}
end
# TODO: multi project
def self.set_project_env_run_list
lambda {
check_privileges("project", "w")
list = create_object_from_json_body(Array)
check_array(list, "Body must contains not empty array of strings")
project = settings.mongo.project(params[:id])
env = project.deploy_env params[:env]
env.run_list = list
settings.mongo.project_update project
create_response("Updated environment '#{env.identifier}' with run_list '#{env.run_list.inspect}' in project '#{project.id}'")
}
end
def self.delete_project
lambda {
check_privileges("project", "w")
servers = settings.mongo.servers params[:project]
raise DependencyError.new "Deleting #{params[:project]} is forbidden: Project has servers" if !servers.empty?
body = create_object_from_json_body(Hash, true)
deploy_env = unless body.nil?
check_string(body["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true)
end
project = settings.mongo.project(params[:project])
info = if deploy_env.nil?
project.delete
"Project '#{params[:project]}' is deleted"
else
project.remove_env deploy_env
settings.mongo.project_update project
"Project '#{params[:project]}'. Deploy environment '#{deploy_env}' has been deleted"
end
create_response(info)
}
end
def self.deploy_project
lambda {
check_privileges("project", "x")
obj = create_object_from_json_body
check_string(obj["deploy_env"], "Parameter 'deploy_env' should be a not empty string", true)
check_array(obj["servers"], "Parameter 'servers' should be a not empty array of strings", String, true)
project = settings.mongo.project(params[:id])
servers = settings.mongo.servers(params[:id], obj["deploy_env"], obj["servers"], true)
keys = {}
if obj.key?("trace")
stream() do |out|
begin
out << (servers.empty? ? "No reserved servers to deploy\n" : "Deploy servers: '#{servers.map{|s| s.chef_node_name}.join("', '")}'\n")
status = []
servers.each do |s|
logger.debug "Deploy server: #{s.inspect}"
begin
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
out << e.message + "\n"
status.push 2
next
end
unless keys.key? s.key
k = settings.mongo.key s.key
keys[s.key] = k.path
end
status.push(deploy_server(out, s, keys[s.key]))
end
out << create_status(status)
rescue IOError => e
logger.error e.message
end
end
else
dir = DevopsConfig[:report_dir_v2]
files = []
uri = URI.parse(request.url)
servers.each do |s|
project = begin
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
rescue InvalidPrivileges, RecordNotFound => e
next
end
jid = DeployWorker.perform_async(dir, s.to_hash, [], DevopsConfig.config)
logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s
end
json files
end
}
end
def self.archive_project
lambda {
check_privileges("project", "w")
project = settings.mongo.project(params[:project])
if project.nil?
create_response("Project '#{params[:project]}' not found", nil, 404)
else
settings.mongo.archive_project(params[:project])
info = "Project '#{params[:project]}' has been archived"
create_response(info)
end
}
end
def self.unarchive_project
lambda {
check_privileges("project", "w")
project = settings.mongo.project(params[:project])
if project.nil?
create_response("Project '#{params[:project]}' not found", nil, 404)
else
settings.mongo.unarchive_project(params[:project])
info = "Project '#{params[:project]}' has been unarchived"
create_response(info)
end
}
end
def self.test_project
lambda {
check_privileges("project", "r")
project = settings.mongo.project(params[:id])
env = project.deploy_env params[:env]
logger.info "Test project '#{project.id}' and environment '#{env.identifier}'"
if env.provider == ::Provider::Static::PROVIDER
msg = "Can not test environment with provider '#{::Provider::Static::PROVIDER}'"
Logger.warn msg
return [400, msg]
end
dir = DevopsConfig[:report_dir_v2]
uri = URI.parse(request.url)
p = {
:project => project.id,
:env => env.identifier,
:user => request.env['REMOTE_USER']
}
jid = ProjectTestWorker.perform_async(dir, p, DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE
logger.info "Job '#{jid}' has been created"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files = [uri.to_s]
sleep 1
json files
}
end
def self.create_roles project_id, envs, logger
all_roles = KnifeCommands.roles
return " Can't get roles list" if all_roles.nil?
roles = {:new => [], :error => [], :exist => []}
envs.each do |e|
role_name = KnifeCommands.role_name(project_id, e.identifier)
begin
if all_roles.include? role_name
roles[:exist].push role_name
else
KnifeCommands.create_role role_name, project_id, e.identifier
roles[:new].push role_name
logger.info "Role '#{role_name}' created"
end
rescue => er
roles[:error].push role_name
logger.error "Role '#{role_name}' can not be created: #{er.message}"
end
end
roles
end
def self.create_new_roles old_project, new_project, logger
old_project.deploy_envs.each do |e|
new_project.remove_env(e.identifier)
end
Devops::Version2_0::Handler::Project.create_roles new_project.id, new_project.deploy_envs, logger
end
def self.create_roles_response roles
if roles.is_a?(String)
roles
else
info = ""
info += " Project roles '#{roles[:new].join("', '")}' have been automaticaly created" unless roles[:new].empty?
info += " Project roles '#{roles[:exist].join("', '")}' weren't created because they exist" unless roles[:exist].empty?
info += " Project roles '#{roles[:error].join("', '")}' weren't created because of internal error" unless roles[:error].empty?
info
end
end
end
end
end
end

View File

@ -1,16 +0,0 @@
require "providers/provider_factory"
module Devops
module Version2_0
module Handler
class Provider
def self.get_providers
lambda {
check_privileges("provider", "r")
json ::Provider::ProviderFactory.providers
}
end
end
end
end
end

View File

@ -1,63 +0,0 @@
module Devops
module Version2_0
module Handler
class Report
def self.reports_all
lambda {
options = {}
["project", "deploy_env", "type", "created_by", "date_from", "date_to", "sort", "status", "max_number", "chef_node_name"].each do |k|
options[k] = params[k] unless params[k].nil?
end
attributes_keys = params.keys.select{|k| k =~ /attributes\.*/}
attributes_keys.each do |ak|
options[ak] = params[ak]
end
json Devops::Db.connector.reports(options).map{|r| r.to_hash}
}
end
def self.reports_latest
lambda {
options = {}
["project", "deploy_env", "type", "created_by", "date_from", "date_to", "sort", "status", "chef_node_name"].each do |k|
options[k] = params[k] unless params[k].nil?
end
attributes_keys = params.keys.select{|k| k =~ /attributes\.*/}
attributes_keys.each do |ak|
options[ak] = params[ak]
end
json Devops::Db.connector.latest_reports(options).map{|r| r.to_hash}
}
end
def self.attributes_all
lambda{
json Devops::Db.connector.reports_attributes_values(params["name"])
}
end
def self.report
lambda{
begin
r = Devops::Db.connector.report(params[:id])
file = r.file
return [404, "Report '#{params[:id]}' does not exist"] unless File.exists? file
@text = Rack::Utils.escape_html(File.read(file))
@done = completed?(params[:id])
rescue RecordNotFound => e
if task_status(params[:id]) == Worker::STATUS::IN_QUEUE
@text = "Task '#{params[:id]}' has been queued"
@done = false
else
raise e
end
end
erb :index
}
end
end
end
end
end

View File

@ -1,127 +0,0 @@
require "providers/provider_factory"
require "fileutils"
require "commands/status"
module Devops
module Version2_0
module Handler
class Script
def self.get_scripts
lambda {
check_privileges("script", "r")
res = []
Dir.foreach(DevopsService.config[:scripts_dir]) {|f| res.push(f) unless f.start_with?(".")}
json res
}
end
def self.execute_command
lambda {
check_privileges("script", "x")
user = request.env['REMOTE_USER']
s = ::Devops::Db.connector.server_by_chef_node_name params[:node_name]
::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user
cert = ::Devops::Db.connector.key s.key
cmd = request.body.read
addr = "#{s.remote_user}@#{s.public_ip || s.private_ip}"
ssh_cmd = "ssh -i %s #{addr} '#{cmd}'"
stream() do |out|
begin
out << ssh_cmd % File.basename(cert.path)
out << "\n"
IO.popen((ssh_cmd % cert.path) + " 2>&1") do |so|
while line = so.gets do
out << line
end
end
out << "\nDone"
rescue IOError => e
logger.error e.message
end
end
}
end
def self.run_script
lambda {
check_privileges("script", "x")
file_name = params[:script_name]
@file = File.join(DevopsService.config[:scripts_dir], check_filename(file_name, "Parameter 'script_name' must be a not empty string", false))
halt(404, "File '#{file_name}' does not exist") unless File.exists?(@file)
body = create_object_from_json_body
nodes = check_array(body["nodes"], "Parameter 'nodes' must be a not empty array of strings")
p = check_array(body["params"], "Parameter 'params' should be a not empty array of strings", String, true)
servers = ::Devops::Db.connector.servers_by_names(nodes)
return [404, "No servers found for names '#{nodes.join("', '")}'"] if servers.empty?
user = request.env['REMOTE_USER']
servers.each do |s|
::Devops::Db.connector.check_project_auth s.project, s.deploy_env, user
end
stream() do |out|
begin
status = []
servers.each do |s|
cert = begin
::Devops::Db.connector.key s.key
rescue
out << "No key found for '#{s.chef_node_name}'"
status.push 2
next
end
ssh_cmd = "ssh -i #{cert.path} #{s.remote_user}@#{s.public_ip || s.private_ip} 'bash -s' < %s"
out << "\nRun script on '#{s.chef_node_name}'\n"
unless p.nil?
ssh_cmd += " " + p.join(" ")
end
out << (ssh_cmd % [params[:script_name]])
out << "\n"
begin
IO.popen( (ssh_cmd % [@file]) + " 2>&1") do |so|
while line = so.gets do
out << line
end
so.close
status.push $?.to_i
end
rescue IOError => e
logger.error e.message
out << e.message
status.push 3
end
end
out << create_status(status)
rescue IOError => e
logger.error e.message
end
end
}
end
def self.create_script
lambda {
check_privileges("script", "w")
file_name = params[:script_name]
file = File.join(settings.scripts_dir, check_filename(file_name, "Parameter 'script_name' must be a not empty string"))
halt_response("File '#{file_name}' already exist") if File.exists?(file)
File.open(file, "w") {|f| f.write(request.body.read)}
create_response("File '#{params[:script_name]}' created", nil, 201)
}
end
def self.delete_script
lambda {
check_privileges("script", "w")
file_name = params[:script_name]
file = File.join(settings.scripts_dir, check_filename(file_name, "Parameter 'script_name' must be a not empty string"))
halt_response("File '#{file_name}' does not exist", 404) unless File.exists?(file)
FileUtils.rm(file)
create_response("File '#{params[:script_name]}' deleted")
}
end
end
end
end
end

View File

@ -1,349 +0,0 @@
require 'rufus-scheduler'
require "uri"
require "commands/status"
require "commands/server"
require "commands/bootstrap_templates"
require "commands/knife_commands"
require "providers/provider_factory"
require "db/mongo/models/server"
require "workers/create_server_worker"
require "workers/bootstrap_worker"
module Devops
module Version2_0
module Handler
class Server
extend StatusCommands
extend ServerCommands
extend BootstrapTemplatesCommands
scheduler = Rufus::Scheduler.new
def self.get_servers
lambda {
check_privileges("server", "r")
fields = []
if params.key?("fields") and params["fields"].is_a?(Array)
Devops::Model::Server.fields.each do |k|
fields.push k if params["fields"].include?(k)
end
end
reserved = (params.key?("reserved") ? true : nil)
json settings.mongo.servers(nil, nil, nil, reserved, fields).map {|s| s.to_hash}
}
end
def self.get_chef_servers
lambda {
check_privileges("server", "r")
json KnifeCommands.chef_node_list
}
end
def self.get_provider_servers
lambda {
check_privileges("server", "r")
json ::Provider::ProviderFactory.get(params[:provider]).servers
}
end
def self.get_server
lambda {
check_privileges("server", "r")
json Server.get_server_by_key(params[:name], params[:key]).to_hash
}
end
def self.delete_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
s = Server.get_server_by_key(params[:id], key)
### Authorization
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
info, r = delete_server(s, settings.mongo, logger)
create_response(info, r)
}
end
def self.create_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body
user = request.env['REMOTE_USER']
project_name = check_string(body["project"], "Parameter 'project' must be a not empty string")
env_name = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string")
server_name = check_string(body["name"], "Parameter 'name' should be null or not empty string", true)
without_bootstrap = body["without_bootstrap"]
halt_response("Parameter 'without_bootstrap' should be a null or true") unless without_bootstrap.nil? or without_bootstrap == true
force = body["force"]
halt_response("Parameter 'force' should be a null or true") unless force.nil? or force == true
groups = check_array(body["groups"], "Parameter 'groups' should be null or not empty array of string", String, true)
key_name = check_string(body["key"], "Parameter 'key' should be null or not empty string", true)
new_key = settings.mongo.key(key_name) unless key_name.nil?
p = settings.mongo.check_project_auth(project_name, env_name, user)
env = p.deploy_env(env_name)
provider = ::Provider::ProviderFactory.get(env.provider)
Server.check_chef_node_name(server_name, provider) unless server_name.nil?
unless groups.nil?
buf = groups - provider.groups.keys
halt_response("Invalid security groups '#{buf.join("', '")}' for provider '#{provider.name}'") if buf.empty?
end
servers = Server.extract_servers(provider, p, env, body, user, settings.mongo)
if body.key?("trace")
stream() do |out|
begin
status = []
servers.each do |s|
res = create_server_proc.call(out, s, provider, settings.mongo)
status.push res
end
out << create_status(status)
rescue IOError => e
logger.error e.message
end
end
else
dir = DevopsConfig[:report_dir_v2]
files = []
uri = URI.parse(request.url)
servers.each do |s|
h = s.to_hash
h["options"] = s.options
jid = CreateServerWorker.perform_async(dir, env.provider, h, request.env['REMOTE_USER'], DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE
logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
files.push uri.to_s
end
sleep 1
json files
end
}
end
def self.pause_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
s = Server.get_server_by_key(params[:node_name], key)
## Authorization
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
provider = ::Provider::ProviderFactory.get(s.provider)
r = provider.pause_server s
if r.nil?
create_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' is paused")
else
halt_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' can not be paused, It in state '#{r}'", 409)
end
}
end
def self.unpause_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
s = Server.get_server_by_key(params[:node_name], key)
## Authorization
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
provider = ::Provider::ProviderFactory.get(s.provider)
r = provider.unpause_server s
if r.nil?
create_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' is unpaused")
else
halt_response("Server with instance ID '#{s.id}' and node name '#{params[:node_name]}' can not be unpaused, It in state '#{r}'", 409)
end
}
end
def self.reserve_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
s = Server.get_server_by_key(params[:node_name], key)
user = request.env['REMOTE_USER']
settings.mongo.check_project_auth s.project, s.deploy_env, user
halt_response(400, "Server '#{params[:node_name]}' already reserved") unless s.reserved_by.nil?
s.reserved_by = user
settings.mongo.server_update(s)
create_response("Server '#{params[:node_name]}' has been reserved")
}
end
def self.unreserve_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
key = (body.nil? ? nil : body["key"])
s = Server.get_server_by_key(params[:node_name], key)
settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
halt_response(400, "Server '#{params[:node_name]}' is not reserved") if s.reserved_by.nil?
s.reserved_by = nil
settings.mongo.server_update(s)
create_response("Server '#{params[:node_name]}' has been unreserved")
}
end
# TODO: check bootstrap template name
def self.bootstrap_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body(Hash, true)
id = check_string(body["instance_id"], "Parameter 'instance_id' must be a not empty string")
name = check_string(body["name"], "Parameter 'name' should be a not empty string", true)
rl = check_array(body["run_list"], "Parameter 'run_list' should be a not empty array of string", String, true)
unless rl.nil?
validator = Validators::Helpers::RunList.new(rl)
halt_response(validator.message) unless validator.valid?
end
t = check_string(body["bootstrap_template"], "Parameter 'bootstrap_template' should be a not empty string", true)
s = settings.mongo.server_by_instance_id(id)
p = settings.mongo.check_project_auth s.project, s.deploy_env, request.env['REMOTE_USER']
d = p.deploy_env s.deploy_env
provider = ::Provider::ProviderFactory.get(s.provider)
Server.check_chef_node_name(name, provider) unless name.nil?
s.options = {
:run_list => rl || d.run_list,
}
unless t.nil?
templates = get_templates
halt_response("Invalid bootstrap template '#{t}', available values: #{templates.join(", ")}", 400) unless templates.include?(t)
s.options[:bootstrap_template] = t
end
s.chef_node_name = name || provider.create_default_chef_node_name(s)
logger.debug "Chef node name: '#{s.chef_node_name}'"
status = []
if body.key?("trace")
stream() do |out|
begin
cert = settings.mongo.key s.key
logger.debug "Bootstrap certificate path: #{cert.path}"
bootstrap s, out, cert.path, logger
str = nil
r = if check_server(s)
settings.mongo.server_set_chef_node_name s
str = "Server with id '#{s.id}' is bootstraped"
logger.info str
0
else
str = "Server with id '#{s.id}' is not bootstraped"
logger.warn str
1
end
status.push r
out << str
out << "\n"
out << create_status(status)
rescue IOError => e
logger.error e.message
end
end
else
dir = DevopsConfig[:report_dir_v2]
files = []
uri = URI.parse(request.url)
h = s.to_hash
h["options"] = s.options
h["_id"] = s.id
jid = BootstrapWorker.perform_async(dir, d.provider, h, request.env['REMOTE_USER'], DevopsConfig.config)
Worker.set_status jid, Worker::STATUS::IN_QUEUE
logger.info "Job '#{jid}' has been started"
uri.path = "#{DevopsConfig[:url_prefix]}/v2.0/report/" + jid
uri.query = nil
uri.fragment = nil
files.push uri.to_s
sleep 1
json files
end
}
end
def self.add_server
lambda {
check_privileges("server", "w")
body = create_object_from_json_body
project = check_string(body["project"], "Parameter 'project' must be a not empty string")
deploy_env = check_string(body["deploy_env"], "Parameter 'deploy_env' must be a not empty string")
key = check_string(body["key"], "Parameter 'key' must be a not empty string")
remote_user = check_string(body["remote_user"], "Parameter 'remote_user' must be a not empty string")
private_ip = check_string(body["private_ip"], "Parameter 'private_ip' must be a not empty string")
public_ip = check_string(body["public_ip"], "Parameter 'public_ip' should be a not empty string", true)
p = settings.mongo.check_project_auth project, deploy_env, request.env['REMOTE_USER']
d = p.deploy_env(deploy_env)
cert = settings.mongo.key(key)
provider = ::Provider::ProviderFactory.get("static")
s = Devops::Model::Server.new
s.provider = provider.name
s.project = project
s.deploy_env = deploy_env
s.remote_user = remote_user
s.private_ip = private_ip
s.public_ip = public_ip
s.static = true
s.id = "static_#{cert.id}-#{Time.now.to_i}"
s.key = cert.id
settings.mongo.server_insert s
create_response("Server '#{s.id}' has been added")
}
end
def self.get_server_by_key id, key
mongo = Devops::Db.connector
key == "instance" ? mongo.server_by_instance_id(id) : mongo.server_by_chef_node_name(id)
end
def self.check_chef_node_name name, provider
mongo = Devops::Db.connector
mongo.server_by_chef_node_name name
halt(400, "Server with name '#{name}' already exist")
rescue RecordNotFound => e
# server not found - OK
s = provider.servers.detect {|s| s["name"] == name}
halt(400, "#{provider.name} node with name '#{name}' already exist") unless s.nil?
s = KnifeCommands.chef_node_list.detect {|n| n == name}
halt(400, "Chef node with name '#{name}' already exist") unless s.nil?
s = KnifeCommands.chef_client_list.detect {|c| c == name}
halt(400, "Chef client with name '#{name}' already exist") unless s.nil?
end
end
class ExpireHandler
include ServerCommands
def initialize server, logger
@server = server
@logger = logger
end
def call(job)
@logger.info("Removing node '#{@server.chef_node_name}' form project '#{@server.project}' and env '#{@server.deploy_env}'")
begin
delete_server(@server, Devops::Db.connector, @logger)
rescue => e
logger.error "ExpiredHandler error: " + e.message
end
end
end
end
end
end

View File

@ -1,88 +0,0 @@
require 'db/mongo/models/stack/stack_factory'
module Devops
module Version2_0
module Handler
class Stack
def self.get_stacks
lambda {
check_privileges("stack", "r")
stacks = settings.mongo.stacks
json stacks.map(&:to_hash)
}
end
def self.get_stacks_for_provider
lambda {
check_privileges("stack", "r")
check_provider(params[:provider])
stacks = settings.mongo.stacks(provider: params[:provider])
json stacks.map(&:to_hash)
}
end
def self.create_stack
lambda {
check_privileges("stack", "w")
object = create_object_from_json_body
stack_model = Model::StackFactory.create(object['provider'], object)
settings.mongo.stack_insert(stack_model)
create_response "Created", stack_model.to_hash, 201
}
end
def self.get_stack
lambda {
check_privileges("stack", "r")
stack = settings.mongo.stack(params[:stack_id])
json stack.to_hash
}
end
def self.delete_stack
lambda {
check_privileges("stack", "w")
stack = settings.mongo.stack(params[:stack_id])
stack.delete_stack_in_cloud!
settings.mongo.stack_delete(params[:stack_id])
create_response("Stack '#{params[:stack_id]}' has been removed")
}
end
def self.sync_details
lambda {
check_privileges("stack", "w")
stack = settings.mongo.stack(params[:stack_id])
stack.sync_details!
settings.mongo.stack_update(stack)
json stack.to_hash
}
end
def self.resources
lambda {
check_privileges("stack", "r")
stack = settings.mongo.stack(params[:stack_id])
json stack.resources
}
end
def self.resource
lambda {
check_privileges("stack", "r")
stack = settings.mongo.stack(params[:stack_id])
json stack.resource(params[:resource_id])
}
end
end
end
end
end

View File

@ -1,51 +0,0 @@
require 'json'
require 'lib/stack_presets/factory'
require 'workers/stack_sync_worker'
require 'workers/job_starter'
module Devops
module Version2_0
module Handler
class StackPreset
def self.get_presets
lambda {
# check_privileges("stack_presets", "r")
json Devops::StackPresetsFactory.list.map(&:to_hash)
}
end
def self.get_preset
lambda {
# check_privileges("stack_presets", "r")
json Devops::StackPresetsFactory.get(params['id']).to_hash
}
end
def self.apply
lambda {
# check_privileges("stack_presets", "r")
check_privileges('stack_template', 'w')
attrs = create_object_from_json_body
preset = Devops::StackPresetsFactory.get(attrs.fetch('id'))
stack = preset.create_stack_from_preset(attrs)
settings.mongo.stack_insert(stack)
file = JobStarter.start_job(:worker, :sync_stack_till_not_in_progress,
provider: stack.provider,
stack_id: stack.id,
request: request
)
puts "Syncing report is located here: #{file}"
create_response 'Created', stack.to_hash, 201
}
end
end
end
end
end

View File

@ -1,57 +0,0 @@
require 'db/mongo/models/stack_template/stack_template_factory'
module Devops
module Version2_0
module Handler
class StackTemplate
def self.get_stack_templates
lambda {
check_privileges('stack_template', 'r')
stack_templates = settings.mongo.stack_templates
json stack_templates.map(&:to_hash)
}
end
def self.get_stack_templates_for_provider
lambda {
check_privileges('stack_template', 'r')
check_provider(params[:provider])
stack_templates = settings.mongo.stack_templates(params[:provider])
json stack_templates.map(&:to_hash)
}
end
def self.create_stack_template
lambda {
check_privileges('stack_template', 'w')
attrs = create_object_from_json_body
template_model = Model::StackTemplateFactory.create(attrs['provider'], attrs)
settings.mongo.stack_template_insert(template_model)
create_response 'Created', template_model.to_hash, 201
}
end
def self.get_stack_template
lambda {
check_privileges('stack_template', 'r')
stack_template = settings.mongo.stack_template(params[:stack_template_id])
json stack_template.to_hash
}
end
def self.delete_stack_template
lambda {
check_privileges('stack_template', 'w')
settings.mongo.stack_template_delete params[:stack_template_id]
create_response("Template '#{params[:stack_template_id]}' has been removed")
}
end
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More