merge stack_templates and stacks on serverside

This commit is contained in:
Anton Chuchkalov 2015-04-16 18:54:40 +04:00
parent 2a62786299
commit 634d5d6d1f
18 changed files with 326 additions and 84 deletions

View File

@ -12,6 +12,7 @@ require "devops-client/handler/helpers/http_utils"
require "devops-client/handler/helpers/outputtable" require "devops-client/handler/helpers/outputtable"
require "devops-client/handler/helpers/resources_fetcher" require "devops-client/handler/helpers/resources_fetcher"
require "devops-client/handler/helpers/resources_selector" require "devops-client/handler/helpers/resources_selector"
require "devops-client/providers/providers"
class Handler class Handler

View File

@ -0,0 +1,21 @@
module Providers
Ec2 = 'ec2'
Openstack = 'openstack'
Static = 'static'
def self.available
[Ec2, Openstack, Static]
end
def self.functionalities
{
images: [Ec2, Openstack],
stack_templates: [Ec2, Openstack],
stacks: [Ec2, Openstack]
}
end
def self.has_functionality?(provider, functionality)
functionalities.fetch(functionality).include?(provider.to_s)
end
end

View File

@ -2,14 +2,15 @@ module Devops
module Model module Model
class StackBase < MongoModel class StackBase < MongoModel
attr_accessor :id, :project, :deploy_env, :stack_template, :cloud_stack_id, :provider attr_accessor :id, :project, :deploy_env, :stack_template, :cloud_stack_id, :provider, :parameters
types id: {type: String, empty: false}, types id: {type: String, empty: false},
provider: {type: String, empty: false}, provider: {type: String, empty: false},
project: {type: String, empty: false}, project: {type: String, empty: false},
deploy_env: {type: String, empty: false}, deploy_env: {type: String, empty: false},
stack_template: {type: String, empty: false}, stack_template: {type: String, empty: false}
cloud_stack_id: {type: String, empty: false} # cloud_stack_id: {type: String, empty: true}
# TODO: add parameters Hash
def initialize attrs={} def initialize attrs={}
self.id = attrs['id'] self.id = attrs['id']
@ -18,19 +19,22 @@ module Devops
self.deploy_env = attrs['deploy_env'] self.deploy_env = attrs['deploy_env']
self.stack_template = attrs['stack_template'] self.stack_template = attrs['stack_template']
self.cloud_stack_id = attrs['cloud_stack_id'] self.cloud_stack_id = attrs['cloud_stack_id']
self.parameters = attrs['parameters']
self self
end end
def to_hash_without_id def to_hash_without_id
{ {
provider: provider, provider: provider,
project: self.project, project: project,
deploy_env: self.deploy_env, deploy_env: deploy_env,
stack_template: self.stack_template, stack_template: stack_template,
cloud_stack_id: self.cloud_stack_id cloud_stack_id: cloud_stack_id,
parameters: parameters
} }
end end
# attrs should include: # attrs should include:
# - id (String) # - id (String)
# - provider (String) # - provider (String)
@ -51,6 +55,15 @@ module Devops
raise 'override me' raise 'override me'
end end
def delete_stack_in_cloud!
raise 'override me'
end
def template_body
stack_template_model = DevopsService.mongo.stack_template(stack_template)
stack_template_model.template_body
end
end end
end end
end end

View File

@ -16,12 +16,12 @@ module Devops
def self.get_class(provider) def self.get_class(provider)
case provider case provider
when ::Provider::Openstack::PROVIDER when ::Provider::Openstack::PROVIDER
StackOpenstack StackOpenstack
when ::Provider::Ec2::PROVIDER when ::Provider::Ec2::PROVIDER
StackEc2 StackEc2
else else
raise InvalidRecord.new "Invalid provider: '#{provider}'" raise InvalidRecord.new "Invalid provider: '#{provider}'"
end end
end end

View File

@ -3,10 +3,17 @@ module Devops
class StackOpenstack < StackBase class StackOpenstack < StackBase
def create_stack_in_cloud! def create_stack_in_cloud!
provider = ::Provider::ProviderFactory.get('openstack') begin
provider.create_stack(self) provider = Provider::ProviderFactory.get('openstack')
# # create stack in Openstack self.cloud_stack_id = provider.create_stack(self)
# self.cloud_stack_id = '4c712026-dcd5-4664-90b8-0915494c1332' rescue ProviderErrors::NameConflict
raise InvalidRecord.new "Duplicate key error: stack with name '#{self.id}' already exists in cloud"
end
end
def delete_stack_in_cloud!
provider = Provider::ProviderFactory.get('openstack')
provider.delete_stack(self)
end end
end end

View File

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

View File

@ -2,16 +2,50 @@ module Devops
module Model module Model
class StackTemplateEc2 < StackTemplateBase class StackTemplateEc2 < StackTemplateBase
# In Amazon Cloudformation the template file must be stored on an Amazon S3 bucket.
attr_accessor :template_url
types template_url: {type: String, empty: false}
def initialize(attrs)
self.template_url = attrs['template_url']
super(attrs)
end
def to_hash_without_id
super.merge(template_url: template_url)
end
def delete_template_file_from_storage def delete_template_file_from_storage
raise 'Implement me' raise 'Implement me'
end end
class << self class << self
def create(attrs)
template = attrs['template_body']
attrs['template_url'] = generate_template_file_and_upload_to_storage(attrs['id'], template)
super(attrs)
end
private private
def upload_file_to_storage(filename, path) def generate_template_file_and_upload_to_storage(id, json)
"https://s3.amazonaws.com/#{filename}" begin
tempfile = Tempfile.new('foo')
tempfile.write(json)
tempfile.close
secure_filename = "#{id}-#{SecureRandom.hex}.template"
upload_file_to_storage(secure_filename, tempfile.path)
ensure
tempfile.unlink
end
end end
def upload_file_to_storage(filename, file_path)
"https://s3.amazonaws.com/#{filename}"
end
end end
end end

View File

@ -16,12 +16,12 @@ module Devops
def self.get_class(provider) def self.get_class(provider)
case provider case provider
when ::Provider::Openstack::PROVIDER when ::Provider::Openstack::PROVIDER
StackTemplateOpenstack StackTemplateOpenstack
when ::Provider::Ec2::PROVIDER when ::Provider::Ec2::PROVIDER
StackTemplateEc2 StackTemplateEc2
else else
raise InvalidRecord.new "Invalid provider: '#{provider}'" raise InvalidRecord.new "Invalid provider: '#{provider}'"
end end
end end

View File

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

View File

@ -1,4 +1,5 @@
require "fog" require "fog"
Dir["providers/exceptions/*.rb"].each {|file| require file }
module Provider module Provider
class BaseProvider class BaseProvider

View File

@ -0,0 +1,5 @@
module ProviderErrors
class NameConflict < StandardError
end
end

View File

@ -192,9 +192,22 @@ module Provider
end end
def create_stack(stack) def create_stack(stack)
byebug begin
result = orchestration.create_stack(stack.id, {template_url: stack.template_url}) response = orchestration.create_stack(stack.id, {
template: stack.template_body,
tenant_id: connection_options[:openstack_tenant],
parameters: stack.parameters
})
response[:body]['stack']['id']
rescue Excon::Errors::Conflict => e
raise ProviderErrors::NameConflict
end
end end
def delete_stack(stack)
orchestration.delete_stack(stack.id, stack.cloud_stack_id)
end
private private
def convert_groups list def convert_groups list
res = {} res = {}
@ -217,7 +230,7 @@ module Provider
end end
def orchestration def orchestration
@connection ||= Fog::Orchestration::OpenStack.new(connection_options) @connection ||= Fog::Orchestration.new(connection_options)
end end
end end

View File

@ -61,4 +61,12 @@ class Provider::Openstack
] ]
end end
def create_stack(stack)
'4c712026-dcd5-4664-90b8-0915494c1332'
end
def delete_stack(stack)
true
end
end end

View File

@ -18,6 +18,8 @@ require "routes/v2.0/server"
require "routes/v2.0/script" require "routes/v2.0/script"
require "routes/v2.0/status" require "routes/v2.0/status"
require "routes/v2.0/bootstrap_templates" require "routes/v2.0/bootstrap_templates"
require "routes/v2.0/stack_template"
require "routes/v2.0/stack"
require "routes/v2.0/handlers/provider" require "routes/v2.0/handlers/provider"
require "routes/v2.0/handlers/bootstrap_templates" require "routes/v2.0/handlers/bootstrap_templates"
@ -34,6 +36,8 @@ require "routes/v2.0/handlers/status"
require "routes/v2.0/handlers/tag" require "routes/v2.0/handlers/tag"
require "routes/v2.0/handlers/user" require "routes/v2.0/handlers/user"
require "routes/v2.0/handlers/server" require "routes/v2.0/handlers/server"
require "routes/v2.0/handlers/stack_template"
require "routes/v2.0/handlers/stack"
require "routes/routes_container" require "routes/routes_container"
require "auth/devops_auth" require "auth/devops_auth"
@ -61,7 +65,9 @@ module Devops
Devops::Version2_0::Routes::ServerRoutes, Devops::Version2_0::Routes::ServerRoutes,
Devops::Version2_0::Routes::StatusRoutes, Devops::Version2_0::Routes::StatusRoutes,
Devops::Version2_0::Routes::TagRoutes, Devops::Version2_0::Routes::TagRoutes,
Devops::Version2_0::Routes::DeployRoutes Devops::Version2_0::Routes::DeployRoutes,
Devops::Version2_0::Routes::StackTemplateRoutes,
Devops::Version2_0::Routes::StackRoutes
] ]
def init def init

View File

@ -0,0 +1,60 @@
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(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
end
end
end
end

View File

@ -0,0 +1,57 @@
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

View File

@ -0,0 +1,31 @@
module Devops
module Version2_0
module Routes
module StackRoutes
def self.registered(app)
app.after %r{\A/stack_template(/[\w]+)?\z} do
statistic
end
app.get_with_headers '/stacks', :headers => [:accept], &Devops::Version2_0::Handler::Stack.get_stacks
app.get_with_headers '/stacks/provider/:provider', :headers => [:accept], &Devops::Version2_0::Handler::Stack.get_stacks_for_provider
app.post_with_headers "/stack", :headers => [:accept], &Devops::Version2_0::Handler::Stack.create_stack
hash = {}
hash['GET'] = Devops::Version2_0::Handler::Stack.get_stack
hash['DELETE'] = Devops::Version2_0::Handler::Stack.delete_stack
app.multi_routes '/stack/:stack_id', {}, hash
puts "Stack routes initialized"
end
end
end
end
end

View File

@ -0,0 +1,31 @@
module Devops
module Version2_0
module Routes
module StackTemplateRoutes
def self.registered(app)
app.after %r{\A/stack_template(/[\w]+)?\z} do
statistic
end
app.get_with_headers '/stack_templates', :headers => [:accept], &Devops::Version2_0::Handler::StackTemplate.get_stack_templates
app.get_with_headers '/stack_templates/provider/:provider', :headers => [:accept], &Devops::Version2_0::Handler::StackTemplate.get_stack_templates_for_provider
app.post_with_headers "/stack_template", :headers => [:accept], &Devops::Version2_0::Handler::StackTemplate.create_stack_template
hash = {}
hash['GET'] = Devops::Version2_0::Handler::StackTemplate.get_stack_template
hash['DELETE'] = Devops::Version2_0::Handler::StackTemplate.delete_stack_template
app.multi_routes '/stack_template/:stack_template_id', {}, hash
puts "Stack_template routes initialized"
end
end
end
end
end