sinatra modules

This commit is contained in:
amartynov 2014-12-15 14:26:54 +03:00
parent cbf48145cd
commit 79e2811e0c
17 changed files with 1827 additions and 1782 deletions

View File

@ -20,11 +20,6 @@ else
end end
config[:devops_dir] = File.join(ENV["HOME"], ".devops") if config[:devops_dir].nil? config[:devops_dir] = File.join(ENV["HOME"], ".devops") if config[:devops_dir].nil?
puts "Devops home: #{config[:devops_dir]}"
unless File.exists?(config[:devops_dir])
FileUtils.mkdir_p config[:devops_dir]
puts "Directory '#{config[:devops_dir]}' has been created"
end
config[:report_dir_v2] = File.expand_path(File.join(config[:devops_dir], "report", "v2")) unless config[:report_dir_v2] config[:report_dir_v2] = File.expand_path(File.join(config[:devops_dir], "report", "v2")) unless config[:report_dir_v2]
[ [

View File

@ -21,13 +21,32 @@ require "routes/v2.0/user"
class DevopsService < Sinatra::Base class DevopsService < Sinatra::Base
helpers Sinatra::Streaming helpers Sinatra::Streaming
helpers Sinatra::Version2_0::Helpers helpers Devops::Version2_0::Helpers
register Sinatra::Version2_0::Core::ProviderRoutes register Devops::Version2_0::Core::ProviderRoutes
register Sinatra::Version2_0::Core::UserRoutes register Devops::Version2_0::Core::BootstrapTemplatesRoutes
register Devops::Version2_0::Core::UserRoutes
register Devops::Version2_0::Core::FilterRoutes
register Devops::Version2_0::Core::FlavorRoutes
register Devops::Version2_0::Core::GroupRoutes
register Devops::Version2_0::Core::ImageRoutes
register Devops::Version2_0::Core::KeyRoutes
register Devops::Version2_0::Core::NetworkRoutes
register Devops::Version2_0::Core::ProjectRoutes
register Devops::Version2_0::Core::ScriptRoutes
register Devops::Version2_0::Core::ServerRoutes
register Devops::Version2_0::Core::StatusRoutes
register Devops::Version2_0::Core::TagRoutes
def initialize config def initialize config
super() super()
puts "Devops home: #{config[:devops_dir]}"
unless File.exists?(config[:devops_dir])
FileUtils.mkdir_p config[:devops_dir]
puts "Directory '#{config[:devops_dir]}' has been created"
end
self.class.set :devops_home, config[:devops_dir]
self.class.set :config, config self.class.set :config, config
@@config = config @@config = config
root = File.dirname(__FILE__) root = File.dirname(__FILE__)
@ -57,7 +76,7 @@ class DevopsService < Sinatra::Base
use Rack::Auth::Basic do |username, password| use Rack::Auth::Basic do |username, password|
begin begin
mongo.user_auth(username, password) settings.mongo.user_auth(username, password)
true true
rescue RecordNotFound => e rescue RecordNotFound => e
false false
@ -70,6 +89,7 @@ class DevopsService < Sinatra::Base
disable :dump_errors disable :dump_errors
disable :show_exceptions disable :show_exceptions
set :logging, Logger::INFO set :logging, Logger::INFO
puts "TODO2"
end end
configure :development do configure :development do
@ -77,6 +97,7 @@ class DevopsService < Sinatra::Base
disable :raise_errors disable :raise_errors
# disable :dump_errors # disable :dump_errors
set :show_exceptions, :after_handler set :show_exceptions, :after_handler
puts "TODO1"
end end
not_found do not_found do

View File

@ -2,10 +2,9 @@ require "json"
require 'sinatra/base' require 'sinatra/base'
require "sinatra/json" require "sinatra/json"
require "providers/provider_factory" require "providers/provider_factory"
module Sinatra module Devops
module Version2_0 module Version2_0
module Helpers module Helpers

View File

@ -1,35 +1,37 @@
require "json" require "json"
require "routes/v2.0/base_routes"
require "providers/provider_factory" require "providers/provider_factory"
require "commands/bootstrap_templates" require "commands/bootstrap_templates"
module Version2_0 module Devops
class BootstrapTemplatesRoutes < BaseRoutes module Version2_0
module Core
module BootstrapTemplatesRoutes
include BootstrapTemplatesCommands extend BootstrapTemplatesCommands
def initialize wrapper def self.registered(app)
super wrapper
puts "Bootstrap templates routes initialized" # Get list of available bootstrap templates
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of strings
# [
# "omnibus"
# ]
app.get "/templates" do
check_headers :accept
check_privileges("templates", "r")
json BootstrapTemplatesRoutes.get_templates
end
puts "Bootstrap templates routes initialized"
end
end
end end
# Get list of available bootstrap templates
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of strings
# [
# "omnibus"
# ]
get "/templates" do
check_headers :accept
check_privileges("templates", "r")
json get_templates
end
end end
end end

View File

@ -1,84 +1,87 @@
require "routes/v2.0/base_routes"
module Version2_0 module Devops
class FilterRoutes < BaseRoutes module Version2_0
module Core
module FilterRoutes
def initialize wrapper def self.registered(app)
super wrapper
puts "Filter routes initialized" app.before "/filter/:provider/image" do
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
app.after "/filter/:provider/image" do
statistic
end
# Get list of images filters for :provider
#
# Devops can works with images from filters list only
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of strings
# - ec2:
# [
# "ami-83e4bcea"
# ]
# - openstack:
# [
# "36dc7618-4178-4e29-be43-286fbfe90f50"
# ]
app.get "/filter/:provider/images" do
check_headers :accept
check_privileges("filter", "r")
check_provider(params[:provider])
json settings.mongo.available_images(params[:provider])
end
# Add image ids to filter for :provider
#
# * *Request*
# - method : PUT
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "image_id"
# ] -> array of image ids to add to filter
#
# * *Returns* : list of images filters for :provider
app.put "/filter/:provider/image" do
create_response("Updated", {:images => settings.mongo.add_available_images(@images, params[:provider])})
end
# Delete image ids from filter for :provider
#
# * *Request*
# - method : DELETE
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "image_id"
# ] -> array of image ids to delete from filter
#
# * *Returns* : list of images filters for :provider
app.delete "/filter/:provider/image" do
create_response("Deleted", {:images => settings.mongo.delete_available_images(@images, params[:provider])})
end
puts "Filter routes initialized"
end
end
end end
before "/filter/:provider/image" do
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
after "/filter/:provider/image" do
statistic
end
# Get list of images filters for :provider
#
# Devops can works with images from filters list only
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of strings
# - ec2:
# [
# "ami-83e4bcea"
# ]
# - openstack:
# [
# "36dc7618-4178-4e29-be43-286fbfe90f50"
# ]
get "/filter/:provider/images" do
check_headers :accept
check_privileges("filter", "r")
check_provider(params[:provider])
json BaseRoutes.mongo.available_images(params[:provider])
end
# Add image ids to filter for :provider
#
# * *Request*
# - method : PUT
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "image_id"
# ] -> array of image ids to add to filter
#
# * *Returns* : list of images filters for :provider
put "/filter/:provider/image" do
create_response("Updated", {:images => BaseRoutes.mongo.add_available_images(@images, params[:provider])})
end
# Delete image ids from filter for :provider
#
# * *Request*
# - method : DELETE
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "image_id"
# ] -> array of image ids to delete from filter
#
# * *Returns* : list of images filters for :provider
delete "/filter/:provider/image" do
create_response("Deleted", {:images => BaseRoutes.mongo.delete_available_images(@images, params[:provider])})
end
end end
end end

View File

@ -1,49 +1,52 @@
require "json" require "json"
require "routes/v2.0/base_routes"
require "providers/provider_factory" require "providers/provider_factory"
module Version2_0 module Devops
class FlavorRoutes < BaseRoutes module Version2_0
module Core
module FlavorRoutes
def initialize wrapper def self.registered(app)
super wrapper
puts "Flavor routes initialized" # Get list of flavors for :provider
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of objects
# - ec2:
# [
# {
# "id": "t1.micro",
# "cores": 2,
# "disk": 0,
# "name": "Micro Instance",
# "ram": 613
# }
# ]
# - openstack:
# [
# {
# "id": "m1.small",
# "v_cpus": 1,
# "ram": 2048,
# "disk": 20
# }
# ]
app.get "/flavors/:provider" do
check_headers :accept
check_privileges("flavor", "r")
check_provider(params[:provider])
p = ::Provider::ProviderFactory.get params[:provider]
json p.flavors
end
puts "Flavor routes initialized"
end
end
end end
# Get list of flavors for :provider
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of objects
# - ec2:
# [
# {
# "id": "t1.micro",
# "cores": 2,
# "disk": 0,
# "name": "Micro Instance",
# "ram": 613
# }
# ]
# - openstack:
# [
# {
# "id": "m1.small",
# "v_cpus": 1,
# "ram": 2048,
# "disk": 20
# }
# ]
get "/flavors/:provider" do
check_headers :accept
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,61 +1,63 @@
# encoding: UTF-8 # encoding: UTF-8
require "json" require "json"
require "routes/v2.0/base_routes"
require "providers/provider_factory" require "providers/provider_factory"
module Version2_0 module Devops
class GroupRoutes < BaseRoutes module Version2_0
module Core
module GroupRoutes
def initialize wrapper def self.registered(app)
super wrapper # Get security groups for :provider
puts "Group routes initialized" #
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* :
# - ec2:
# {
# "default": {
# "description": "default group",
# "id": "sg-565cf93f",
# "rules": [
# {
# "protocol": "tcp",
# "from": 22,
# "to": 22,
# "cidr": "0.0.0.0/0"
# }
# ]
# }
# }
# - openstack:
# {
# "default": {
# "description": "default",
# "rules": [
# {
# "protocol": null,
# "from": null,
# "to": null,
# "cidr": null
# }
# ]
# }
# }
# TODO: vpc support for ec2
app.get "/groups/:provider" do
check_headers :accept
check_privileges("group", "r")
check_provider(params[:provider])
p = ::Provider::ProviderFactory.get params[:provider]
json p.groups(params)
end
puts "Group routes initialized"
end
end
end end
# Get security groups for :provider
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* :
# - ec2:
# {
# "default": {
# "description": "default group",
# "id": "sg-565cf93f",
# "rules": [
# {
# "protocol": "tcp",
# "from": 22,
# "to": 22,
# "cidr": "0.0.0.0/0"
# }
# ]
# }
# }
# - openstack:
# {
# "default": {
# "description": "default",
# "rules": [
# {
# "protocol": null,
# "from": null,
# "to": null,
# "cidr": null
# }
# ]
# }
# }
# TODO: vpc support for ec2
get "/groups/:provider" do
check_headers :accept
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,178 +1,180 @@
require "providers/provider_factory" require "providers/provider_factory"
require "routes/v2.0/base_routes"
require "commands/image" require "commands/image"
module Version2_0 module Devops
class ImageRoutes < BaseRoutes module Version2_0
module Core
module ImageRoutes
include ImageCommands extend ImageCommands
def initialize wrapper def self.registered(app)
super wrapper app.after %r{\A/image(/[\w]+)?\z} do
puts "Image routes initialized" statistic
end end
after %r{\A/image(/[\w]+)?\z} do # Get devops images list
statistic #
end # * *Request*
# - method : GET
# - headers :
# - Accept: application/json
# - parameters:
# - provider=ec2|openstack -> return images for provider
#
# * *Returns* :
# [
# {
# "provider": "openstack",
# "name": "centos-6.4-x86_64",
# "remote_user": "root",
# "bootstrap_template": null,
# "id": "36dc7618-4178-4e29-be43-286fbfe90f50"
# }
# ]
app.get "/images" do
check_headers :accept
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
# Get devops images list # Get raw images for :provider
# #
# * *Request* # * *Request*
# - method : GET # - method : GET
# - headers : # - headers :
# - Accept: application/json # - Accept: application/json
# - parameters: #
# - provider=ec2|openstack -> return images for provider # * *Returns* :
# # - ec2
# * *Returns* : # [
# [ # {
# { # "id": "ami-83e4bcea",
# "provider": "openstack", # "name": "amzn-ami-pv-2013.09.1.x86_64-ebs",
# "name": "centos-6.4-x86_64", # "status": "available"
# "remote_user": "root", # }
# "bootstrap_template": null, # ]
# "id": "36dc7618-4178-4e29-be43-286fbfe90f50" # - openstack
# } # [
# ] # {
get "/images" do # "id": "36dc7618-4178-4e29-be43-286fbfe90f50",
check_headers :accept # "name": "centos-6.4-x86_64",
check_privileges("image", "r") # "status": "ACTIVE"
check_provider(params[:provider]) if params[:provider] # }
images = BaseRoutes.mongo.images(params[:provider]) # ]
json(images.map {|i| i.to_hash}) app.get "/images/provider/:provider" do
end check_headers :accept
check_privileges("image", "r")
check_provider(params[:provider])
json get_images(settings.mongo, params[:provider])
end
# Get raw images for :provider # Get devops image by id
# #
# * *Request* # * *Request*
# - method : GET # - method : GET
# - headers : # - headers :
# - Accept: application/json # - Accept: application/json
# #
# * *Returns* : # * *Returns* :
# - ec2 # {
# [ # "provider": "openstack",
# { # "name": "centos-6.4-x86_64",
# "id": "ami-83e4bcea", # "remote_user": "root",
# "name": "amzn-ami-pv-2013.09.1.x86_64-ebs", # "bootstrap_template": null,
# "status": "available" # "id": "36dc7618-4178-4e29-be43-286fbfe90f50"
# } # }
# ] app.get "/image/:image_id" do
# - openstack check_headers :accept
# [ check_privileges("image", "r")
# { json settings.mongo.image(params[:image_id])
# "id": "36dc7618-4178-4e29-be43-286fbfe90f50", end
# "name": "centos-6.4-x86_64",
# "status": "ACTIVE"
# }
# ]
get "/images/provider/:provider" do
check_headers :accept
check_privileges("image", "r")
check_provider(params[:provider])
json get_images(BaseRoutes.mongo, params[:provider])
end
# Get devops image by id # Create devops image
# #
# * *Request* # * *Request*
# - method : GET # - method : POST
# - headers : # - headers :
# - Accept: application/json # - Accept: application/json
# # - Content-Type: application/json
# * *Returns* : # - body :
# { # {
# "provider": "openstack", # "id": "image id",
# "name": "centos-6.4-x86_64", # "provider": "image provider",
# "remote_user": "root", # "remote_user": "user", -> the ssh username
# "bootstrap_template": null, # "bootstrap_template": null, -> specific bootstrap template name or nil
# "id": "36dc7618-4178-4e29-be43-286fbfe90f50" # "name": "image name"
# } # }
get "/image/:image_id" do #
check_headers :accept # * *Returns* :
check_privileges("image", "r") # 201 - Created
json BaseRoutes.mongo.image(params[:image_id]) app.post "/image" do
end check_headers
check_privileges("image", "w")
image = create_object_from_json_body
settings.mongo.image_insert Image.new(image)
create_response "Created", nil, 201
end
# Create devops image # Update devops image
# #
# * *Request* # * *Request*
# - method : POST # - method : PUT
# - headers : # - headers :
# - Accept: application/json # - Accept: application/json
# - Content-Type: application/json # - Content-Type: application/json
# - body : # - body :
# { # {
# "id": "image id", # "id": "image id",
# "provider": "image provider", # "provider": "image provider",
# "remote_user": "user", -> the ssh username # "remote_user": "user" -> the ssh username
# "bootstrap_template": null, -> specific bootstrap template name or nil # "bootstrap_template": null -> specific bootstrap template name or nil
# "name": "image name" # "name": "image name"
# } # }
# #
# * *Returns* : # * *Returns* :
# 201 - Created # 200 - Updated
post "/image" do app.put "/image/:image_id" do
check_headers check_headers
check_privileges("image", "w") check_privileges("image", "w")
image = create_object_from_json_body settings.mongo.image params[:image_id]
BaseRoutes.mongo.image_insert Image.new(image) image = Image.new(create_object_from_json_body)
create_response "Created", nil, 201 image.id = params[:image_id]
end settings.mongo.image_update image
create_response("Image '#{params[:image_id]}' has been updated")
end
# Update devops image # Delete devops image
# #
# * *Request* # * *Request*
# - method : PUT # - method : DELETE
# - headers : # - headers :
# - Accept: application/json # - Accept: application/json
# - Content-Type: application/json #
# - body : # * *Returns* :
# { # 200 - Deleted
# "id": "image id", app.delete "/image/:image_id" do
# "provider": "image provider", check_headers
# "remote_user": "user" -> the ssh username check_privileges("image", "w")
# "bootstrap_template": null -> specific bootstrap template name or nil projects = settings.mongo.projects_by_image params[:image_id]
# "name": "image name" unless projects.empty?
# } ar = []
# projects.each do |p|
# * *Returns* : ar += p.deploy_envs.select{|e| e.image == params[:image_id]}.map{|e| "#{p.id}.#{e.identifier}"}
# 200 - Updated end
put "/image/:image_id" do raise DependencyError.new "Deleting is forbidden: Image is used in #{ar.join(", ")}"
check_headers end
check_privileges("image", "w")
BaseRoutes.mongo.image params[:image_id]
image = Image.new(create_object_from_json_body)
image.id = params[:image_id]
BaseRoutes.mongo.image_update image
create_response("Image '#{params[:image_id]}' has been updated")
end
# Delete devops image r = settings.mongo.image_delete params[:image_id]
# create_response("Image '#{params[:image_id]}' has been removed")
# * *Request* end
# - method : DELETE
# - headers : puts "Image routes initialized"
# - Accept: application/json
#
# * *Returns* :
# 200 - Deleted
delete "/image/:image_id" do
check_headers
check_privileges("image", "w")
projects = BaseRoutes.mongo.projects_by_image params[:image_id]
unless projects.empty?
ar = []
projects.each do |p|
ar += p.deploy_envs.select{|e| e.image == params[:image_id]}.map{|e| "#{p.id}.#{e.identifier}"}
end end
raise DependencyError.new "Deleting is forbidden: Image is used in #{ar.join(", ")}"
end end
r = BaseRoutes.mongo.image_delete params[:image_id]
create_response("Image '#{params[:image_id]}' has been removed")
end end
end end
end end

View File

@ -3,108 +3,111 @@ require "db/exceptions/invalid_record"
require "db/mongo/models/key" require "db/mongo/models/key"
require "fileutils" require "fileutils"
module Version2_0 module Devops
class KeyRoutes < BaseRoutes module Version2_0
module Core
module KeyRoutes
def initialize wrapper def self.registered(app)
super wrapper app.before %r{\A/key(/[\w]+)?\z} do
puts "Key routes initialized" if request.delete?
end check_headers :accept
else
check_headers :accept, :content_type
end
check_privileges("key", "w")
end
app.after %r{\A/key(/[\w]+)?\z} do
statistic
end
# 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 "/keys" do
check_headers :accept
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
# 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 "/key" do
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(DevopsService.config[: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 = Key.new({"path" => file_name, "id" => kname})
settings.mongo.key_insert key
create_response("Created", nil, 201)
end
# Delete ssh key from devops server
#
# * *Request*
# - method : DELETE
# - headers :
# - Accept: application/json
#
# * *Returns* :
# 200 - Deleted
app.delete "/key/:key" do
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
puts "Key routes initialized"
end
before %r{\A/key(/[\w]+)?\z} do
if request.delete?
check_headers :accept
else
check_headers :accept, :content_type
end end
check_privileges("key", "w")
end end
after %r{\A/key(/[\w]+)?\z} do
statistic
end
# 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"
# }
# ]
get "/keys" do
check_headers :accept
check_privileges("key", "r")
keys = BaseRoutes.mongo.keys.map {|i| i.to_hash}
keys.each {|k| k.delete("path")} # We should not return path to the key
json 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
post "/key" do
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(DevopsService.config[: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 = Key.new({"path" => file_name, "id" => kname})
BaseRoutes.mongo.key_insert key
create_response("Created", nil, 201)
end
# Delete ssh key from devops server
#
# * *Request*
# - method : DELETE
# - headers :
# - Accept: application/json
#
# * *Returns* :
# 200 - Deleted
delete "/key/:key" do
servers = BaseRoutes.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 = BaseRoutes.mongo.key params[:key]
begin
FileUtils.rm(k.path)
rescue
logger.error "Missing key file for #{params[:key]} - #{k.filename}"
end
r = BaseRoutes.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,49 +1,51 @@
# encoding: UTF-8 # encoding: UTF-8
require "json" require "json"
require "routes/v2.0/base_routes"
require "providers/provider_factory" require "providers/provider_factory"
module Version2_0 module Devops
class NetworkRoutes < BaseRoutes module Version2_0
module Core
module NetworkRoutes
def initialize wrapper def self.registered(app)
super wrapper # Get list of networks for :provider
puts "Network routes initialized" #
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of strings
# - ec2:
# [
# {
# "cidr": "0.0.0.0/16",
# "vpcId": "vpc-1",
# "subnetId": "subnet-1",
# "name": "subnet-1",
# "zone": "us-east-1a"
# }
# ]
# - openstack:
# [
# {
# "cidr": "0.0.0.0/16",
# "name": "private",
# "id": "b14f8df9-ac27-48e2-8d65-f7ef78dc2654"
# }
# ]
app.get "/networks/:provider" do
check_headers :accept
check_privileges("network", "r")
check_provider(params[:provider])
p = ::Provider::ProviderFactory.get params[:provider]
json p.networks_detail
end
puts "Network routes initialized"
end
end
end end
# Get list of networks for :provider
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* : array of strings
# - ec2:
# [
# {
# "cidr": "0.0.0.0/16",
# "vpcId": "vpc-1",
# "subnetId": "subnet-1",
# "name": "subnet-1",
# "zone": "us-east-1a"
# }
# ]
# - openstack:
# [
# {
# "cidr": "0.0.0.0/16",
# "name": "private",
# "id": "b14f8df9-ac27-48e2-8d65-f7ef78dc2654"
# }
# ]
get "/networks/:provider" do
check_headers :accept
check_privileges("network", "r")
check_provider(params[:provider])
p = ::Provider::ProviderFactory.get params[:provider]
json p.networks_detail
end
end end
end end

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,14 @@
# encoding: UTF-8 # encoding: UTF-8
require 'sinatra/base'
require "json" require "json"
require "routes/v2.0/base_routes"
require "providers/provider_factory" require "providers/provider_factory"
module Sinatra module Devops
module Version2_0 module Version2_0
module Core module Core
module ProviderRoutes module ProviderRoutes
def self.registered(app) def self.registered(app)
puts "Provider routes initialized"
# Get devops providers # Get devops providers
# #
@ -31,6 +27,8 @@ module Sinatra
check_privileges("provider", "r") check_privileges("provider", "r")
json ::Provider::ProviderFactory.providers json ::Provider::ProviderFactory.providers
end end
puts "Provider routes initialized"
end end
end end
end end

View File

@ -1,181 +1,184 @@
require "providers/provider_factory" require "providers/provider_factory"
require "routes/v2.0/base_routes"
require "fileutils" require "fileutils"
require "commands/status" require "commands/status"
module Version2_0 module Devops
class ScriptRoutes < BaseRoutes module Version2_0
module Core
module ScriptRoutes
include StatusCommands extend StatusCommands
def initialize wrapper def self.registered(app)
super wrapper app.before "/script/:script_name" do
puts "Script routes initialized" check_headers :accept
end check_privileges("script", "w")
file_name = params[:script_name]
before "/script/:script_name" do @file = File.join(DevopsService.config[:scripts_dir], check_filename(file_name, "Parameter 'script_name' must be a not empty string"))
check_headers :accept if request.put?
check_privileges("script", "w") halt_response("File '#{file_name}' already exist") if File.exists?(@file)
file_name = params[:script_name] elsif request.delete?
@file = File.join(DevopsService.config[: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)
if request.put?
halt_response("File '#{file_name}' already exist") if File.exists?(@file)
elsif request.delete?
halt_response("File '#{file_name}' does not exist", 404) unless File.exists?(@file)
end
end
after %r{\A/script/((command|run)/)?[\w]+\z} do
statistic
end
# Get scripts names
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* :
# [
# "script_1"
# ]
get "/scripts" do
check_headers :accept
check_privileges("script", "r")
res = []
Dir.foreach(DevopsService.config[:scripts_dir]) {|f| res.push(f) unless f.start_with?(".")}
json res
end
# Run command on node :node_name
#
# * *Request*
# - method : POST
# - body :
# command to run
#
# * *Returns* : text stream
post "/script/command/:node_name" do
check_privileges("script", "x")
user = request.env['REMOTE_USER']
s = BaseRoutes.mongo.server_by_chef_node_name params[:node_name]
BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, user
cert = BaseRoutes.mongo.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
end end
out << "\nDone"
rescue IOError => e
logger.error e.message
end
end
end
# Run script :script_name on nodes app.after %r{\A/script/((command|run)/)?[\w]+\z} do
# statistic
# * *Request* end
# - method : POST
# - headers :
# - Content-Type: application/json
# - body :
# {
# "nodes": [], -> array of nodes names
# "params": [] -> array of script arguments
# }
#
# * *Returns* : text stream
post "/script/run/:script_name" do
check_headers :content_type
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 = BaseRoutes.mongo.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|
BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, user
end
stream() do |out|
begin
status = []
servers.each do |s|
cert = begin
BaseRoutes.mongo.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 # Get scripts names
IO.popen( (ssh_cmd % [@file]) + " 2>&1") do |so| #
while line = so.gets do # * *Request*
out << line # - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* :
# [
# "script_1"
# ]
app.get "/scripts" do
check_headers :accept
check_privileges("script", "r")
res = []
Dir.foreach(DevopsService.config[:scripts_dir]) {|f| res.push(f) unless f.start_with?(".")}
json res
end
# Run command on node :node_name
#
# * *Request*
# - method : POST
# - body :
# command to run
#
# * *Returns* : text stream
app.post "/script/command/:node_name" do
check_privileges("script", "x")
user = request.env['REMOTE_USER']
s = BaseRoutes.mongo.server_by_chef_node_name params[:node_name]
BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, user
cert = BaseRoutes.mongo.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 end
so.close out << "\nDone"
status.push $?.to_i rescue IOError => e
logger.error e.message
end end
rescue IOError => e
logger.error e.message
out << e.message
status.push 3
end end
end end
out << create_status(status)
rescue IOError => e # Run script :script_name on nodes
logger.error e.message #
# * *Request*
# - method : POST
# - headers :
# - Content-Type: application/json
# - body :
# {
# "nodes": [], -> array of nodes names
# "params": [] -> array of script arguments
# }
#
# * *Returns* : text stream
app.post "/script/run/:script_name" do
check_headers :content_type
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 = BaseRoutes.mongo.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|
BaseRoutes.mongo.check_project_auth s.project, s.deploy_env, user
end
stream() do |out|
begin
status = []
servers.each do |s|
cert = begin
BaseRoutes.mongo.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
# Create script :script_name
#
# * *Request*
# - method : PUT
# - headers :
# - Accept: application/json
# - body : script content
#
# * *Returns* :
# 201 - Created
app.put "/script/:script_name" do
File.open(@file, "w") {|f| f.write(request.body.read)}
create_response("File '#{params[:script_name]}' created", nil, 201)
end
# Delete script :script_name
#
# * *Request*
# - method : Delete
# - headers :
# - Accept: application/json
#
# * *Returns* :
# 200 - Deleted
app.delete "/script/:script_name" do
FileUtils.rm(@file)
create_response("File '#{params[:script_name]}' deleted")
end
puts "Script routes initialized"
end end
end end
end end
# Create script :script_name
#
# * *Request*
# - method : PUT
# - headers :
# - Accept: application/json
# - body : script content
#
# * *Returns* :
# 201 - Created
put "/script/:script_name" do
File.open(@file, "w") {|f| f.write(request.body.read)}
create_response("File '#{params[:script_name]}' created", nil, 201)
end
# Delete script :script_name
#
# * *Request*
# - method : Delete
# - headers :
# - Accept: application/json
#
# * *Returns* :
# 200 - Deleted
delete "/script/:script_name" do
FileUtils.rm(@file)
create_response("File '#{params[:script_name]}' deleted")
end
end end
end end

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,24 @@
require "json" require "json"
require "routes/v2.0/base_routes"
require "sidekiq" require "sidekiq"
module Version2_0 module Devops
class StatusRoutes < BaseRoutes module Version2_0
module Core
module StatusRoutes
def initialize wrapper def self.registered(app)
super wrapper app.get "/status/:id" do
puts "Status routes initialized" r = Sidekiq.redis do |connection|
end connection.hget("devops", params[:id])
end
return [404, "Job with id '#{params[:id]}' not found"] if r.nil?
r
end
puts "Status routes initialized"
end
get "/status/:id" do
r = Sidekiq.redis do |connection|
connection.hget("devops", params[:id])
end end
return [404, "Job with id '#{params[:id]}' not found"] if r.nil?
r
end end
end end
end end

View File

@ -1,87 +1,91 @@
require "commands/knife_commands" require "commands/knife_commands"
module Version2_0 module Devops
class TagRoutes < BaseRoutes module Version2_0
module Core
module TagRoutes
def initialize wrapper def self.registered(app)
super wrapper app.before "/tags/:node_name" do
puts "Tag routes initialized" if request.get?
end check_headers :accept
check_privileges("server", "r")
else
check_headers :accept, :content_type
check_privileges("server", "w")
@tags = create_object_from_json_body(Array)
check_array(@tags, "Request body should be a not empty array of strings")
end
server = BaseRoutes.mongo.server_by_chef_node_name(params[:node_name])
halt_response("No servers found for name '#{params[:node_name]}'", 404) if server.nil?
@chef_node_name = server.chef_node_name
end
app.after "/tags/:node_name" do
statistic
end
# Get tags list for :node_name
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* :
# [
# "tag_1"
# ]
app.get "/tags/:node_name" do
json(KnifeCommands.tags_list(@chef_node_name))
end
# Set tags list to :node_name
#
# * *Request*
# - method : POST
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "tag_1"
# ]
#
# * *Returns* :
# 200
app.post "/tags/:node_name" do
tagsStr = @tags.join(" ")
cmd = KnifeCommands.tags_create(@chef_node_name, tagsStr)
halt_response("Error: Cannot add tags #{tagsStr} to server #{@chef_node_name}", 500) unless cmd[1]
create_response("Set tags for #{@chef_node_name}: #{tagsStr}")
end
# Delete tags from :node_name
#
# * *Request*
# - method : DELETE
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "tag_1"
# ]
#
# * *Returns* :
# 200
app.delete "/tags/:node_name" do
tagsStr = @tags.join(" ")
cmd = KnifeCommands.tags_delete(@chef_node_name, tagsStr)
halt_response("Cannot delete tags #{tagsStr} from server #{@chef_node_name}: #{cmd[0]}", 500) unless cmd[1]
create_response("Deleted tags for #{@chef_node_name}: #{tagsStr}")
end
puts "Tag routes initialized"
end
before "/tags/:node_name" do
if request.get?
check_headers :accept
check_privileges("server", "r")
else
check_headers :accept, :content_type
check_privileges("server", "w")
@tags = create_object_from_json_body(Array)
check_array(@tags, "Request body should be a not empty array of strings")
end end
server = BaseRoutes.mongo.server_by_chef_node_name(params[:node_name])
halt_response("No servers found for name '#{params[:node_name]}'", 404) if server.nil?
@chef_node_name = server.chef_node_name
end
after "/tags/:node_name" do
statistic
end
# Get tags list for :node_name
#
# * *Request*
# - method : GET
# - headers :
# - Accept: application/json
#
# * *Returns* :
# [
# "tag_1"
# ]
get "/tags/:node_name" do
json(KnifeCommands.tags_list(@chef_node_name))
end
# Set tags list to :node_name
#
# * *Request*
# - method : POST
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "tag_1"
# ]
#
# * *Returns* :
# 200
post "/tags/:node_name" do
tagsStr = @tags.join(" ")
cmd = KnifeCommands.tags_create(@chef_node_name, tagsStr)
halt_response("Error: Cannot add tags #{tagsStr} to server #{@chef_node_name}", 500) unless cmd[1]
create_response("Set tags for #{@chef_node_name}: #{tagsStr}")
end
# Delete tags from :node_name
#
# * *Request*
# - method : DELETE
# - headers :
# - Accept: application/json
# - Content-Type: application/json
# - body :
# [
# "tag_1"
# ]
#
# * *Returns* :
# 200
delete "/tags/:node_name" do
tagsStr = @tags.join(" ")
cmd = KnifeCommands.tags_delete(@chef_node_name, tagsStr)
halt_response("Cannot delete tags #{tagsStr} from server #{@chef_node_name}: #{cmd[0]}", 500) unless cmd[1]
create_response("Deleted tags for #{@chef_node_name}: #{tagsStr}")
end end
end end
end end

View File

@ -1,7 +1,7 @@
require "db/exceptions/invalid_record" require "db/exceptions/invalid_record"
require "db/mongo/models/user" require "db/mongo/models/user"
module Sinatra module Devops
module Version2_0 module Version2_0
module Core module Core
module UserRoutes module UserRoutes