Skip to content

Commit

Permalink
Merge branch 'feature/service-scaling-81050026'
Browse files Browse the repository at this point in the history
  • Loading branch information
rheinwein committed Nov 10, 2014
2 parents b3d97f5 + 19e1cb4 commit c4830cc
Show file tree
Hide file tree
Showing 17 changed files with 954 additions and 56 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
coverage/
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--color
11 changes: 11 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,14 @@ ruby '2.1.2'
gem 'sinatra', require: 'sinatra/base'
gem 'sinatra-contrib'
gem 'fleet-api', '0.6.1', require: 'fleet'

group :test do
gem 'rspec'
gem 'its'
gem 'simplecov'
end

group :development do
gem 'pry', require: false
gem 'pry-nav', require: false
end
35 changes: 35 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,49 @@ GEM
remote: https://rubygems.org/
specs:
backports (3.6.0)
coderay (1.1.0)
diff-lcs (1.2.5)
docile (1.1.5)
faraday (0.8.9)
multipart-post (~> 1.2.0)
faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
fleet-api (0.6.1)
faraday (= 0.8.9)
faraday_middleware (= 0.9.0)
its (0.2.0)
rspec-core
method_source (0.8.2)
multi_json (1.10.1)
multipart-post (1.2.0)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pry-nav (0.2.4)
pry (>= 0.9.10, < 0.11.0)
rack (1.5.2)
rack-protection (1.5.3)
rack
rack-test (0.6.2)
rack (>= 1.0)
rspec (3.1.0)
rspec-core (~> 3.1.0)
rspec-expectations (~> 3.1.0)
rspec-mocks (~> 3.1.0)
rspec-core (3.1.7)
rspec-support (~> 3.1.0)
rspec-expectations (3.1.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.1.0)
rspec-mocks (3.1.3)
rspec-support (~> 3.1.0)
rspec-support (3.1.2)
simplecov (0.8.2)
docile (~> 1.1.0)
multi_json
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
sinatra (1.4.5)
rack (~> 1.4)
rack-protection (~> 1.4)
Expand All @@ -27,12 +56,18 @@ GEM
rack-test
sinatra (~> 1.4.0)
tilt (~> 1.3)
slop (3.6.0)
tilt (1.4.1)

PLATFORMS
ruby

DEPENDENCIES
fleet-api (= 0.6.1)
its
pry
pry-nav
rspec
simplecov
sinatra
sinatra-contrib
1 change: 1 addition & 0 deletions app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

require 'app/models'
require 'app/routes'
require 'app/utils'

module FleetAdapter
class App < Sinatra::Application
Expand Down
165 changes: 116 additions & 49 deletions app/models/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,31 @@ module FleetAdapter
module Models
class Service

using FleetAdapter::StringExtensions

attr_accessor :id, :name, :source, :links, :command, :ports,
:expose, :environment, :volumes, :deployment
:expose, :environment, :volumes, :deployment, :prefix

attr_reader :status

def self.find(id)
new('id' => id).tap(&:refresh)
new(id: id).tap(&:refresh)
end

def self.create_all(attrs)
attrs.map { |service_attrs| Service.new(service_attrs).tap(&:load) }
attrs.map { |service_attrs| self.create(service_attrs) }.flatten
end

def self.create(attrs)
new(attrs).tap(&:load)
count = attrs.fetch(:deployment, {}).fetch(:count, 1).to_i

count.times.map do |i|
new(attrs, i + 1).tap(&:load)
end
end

def initialize(attrs)
self.name = attrs[:name]
self.id = attrs[:id] || (name.end_with?(".service") ? name : "#{name}.service")
def initialize(attrs, index=nil)
self.name = attrs[:name].to_s.sanitize
self.source = attrs[:source]
self.links = attrs[:links] || []
self.command= attrs[:command]
Expand All @@ -32,6 +37,15 @@ def initialize(attrs)
self.environment = attrs[:environment] || []
self.volumes = attrs[:volumes] || []
self.deployment = attrs[:deployment] || {}
self.prefix = self.name

self.name += "@#{index}" if self.deployment[:count] && self.deployment[:count] != 1

if attrs[:id]
self.id = attrs[:id]
else
self.id = "#{name}.service"
end
end

def load
Expand Down Expand Up @@ -61,72 +75,77 @@ def refresh
end
end

def docker_run_string
[
'/usr/bin/docker run',
'--rm',
"--name #{name}",
link_flags,
port_flags,
expose_flags,
environment_flags,
volume_flags,
source,
command
].flatten.compact.join(' ').strip
end
private

def service_def
{
'Unit' => unit_block,
'Service' => service_block,
'X-Fleet' => fleet_block
}
end

def unit_block
unit_block = {}

if links
dep_services = links.map do |link|
"#{link[:name]}.service"
unless links.empty?
dependencies = links.map do |link|
"#{link[:name].sanitize}.service"
end.join(' ')

unit_block['After'] = dep_services
unit_block['Requires'] = dep_services
unit_block['After'] = dependencies
unit_block['Wants'] = dependencies
end

docker_rm = "-/usr/bin/docker rm #{name}"
service_block = {
return unit_block
end

def service_block
docker_rm = "-/usr/bin/docker rm #{prefix}"
service_registration = "/usr/bin/etcdctl set app/#{name.upcase}/#{name.upcase}_SERVICE_HOST ${COREOS_PRIVATE_IPV4}"

{
# A hack to be able to have two ExecStartPre values
'EnvironmentFile'=>'/etc/environment',
:ExecStartPre => "#{service_registration}",
'ExecStartPre' => "-/usr/bin/docker pull #{source}",
'ExecStart' => docker_run_string,
'ExecStart' => "-/bin/bash -c \"#{docker_run_string}\"",
'ExecStartPost' => docker_rm,
'ExecStop' => "-/usr/bin/docker kill #{name}",
'ExecStop' => "-/bin/bash -c \"/usr/bin/etcdctl rm app/#{name.upcase} --recursive && /usr/bin/docker kill #{prefix}\"",
'ExecStopPost' => docker_rm,
'Restart' => 'always',
'RestartSec' => '10',
'TimeoutStartSec' => '5min'
}

{
'Unit' => unit_block,
'Service' => service_block
}
end

private
def fleet_block
{ 'Conflicts' => id.gsub(/@\d\./, "@*.") }
end

def link_flags
return unless links
links.map do |link|
option = '--link '
option << link[:name]
option << ':'
option << (link[:alias] ? link[:alias] : link[:name])
option
end
def docker_run_string
[
'/usr/bin/docker run',
'--rm',
"--name #{name.split('@').first}",
port_flags,
expose_flags,
environment_flags,
link_flags,
volume_flags,
source,
command
].flatten.compact.join(' ').strip
end

def port_flags
return unless ports

ports.map do |port|
option = '-p '
if port[:hostInterface] || port[:hostPort]
option << "#{port[:hostInterface]}:" if port[:hostInterface]
option << "#{port[:hostPort]}" if port[:hostport]
option << ':'
option << "#{port[:hostPort]}:" unless port[:hostPort].to_s.empty?
end
option << "#{port[:containerPort]}"
option << '/udp' if port[:protocol] && port[:protocol].upcase == 'UDP'
Expand All @@ -139,16 +158,64 @@ def expose_flags
expose.map { |exposed_port| "--expose #{exposed_port}" }
end

def link_flags
return unless links
link_vars = []

# add environment variables for linked services for etcd discovery
links.each do |link|
link_alias = link[:alias].upcase if link[:alias]
link_name = link[:name].sanitize.upcase

min_port = link[:exposed_ports].sort_by { |exposed_port| exposed_port[:containerPort] }.first

link_vars.push(
{
variable: (link_alias ? "#{link_alias}_SERVICE_HOST" : "#{link_name}_SERVICE_HOST").upcase,
value: "`/usr/bin/etcdctl get app/#{link_name}/#{link_name}_SERVICE_HOST`"
},
{
variable: (link_alias ? "#{link_alias}_PORT" : "#{link_name}_PORT").upcase,
value: "#{min_port[:protocol]}://`/usr/bin/etcdctl get app/#{link_name}/#{link_name}_SERVICE_HOST`:#{min_port[:hostPort]}"
}
)

# Docker-esque container linking variables
link[:exposed_ports].each do |exposed_port|
link_vars.push(
{
variable: (link_alias ? "#{link_alias}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}" : "#{link_name}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}").upcase,
value: "#{exposed_port[:protocol]}://`/usr/bin/etcdctl get app/#{link_name}/#{link_name}_SERVICE_HOST`:#{exposed_port[:hostPort]}"
},
{
variable: (link_alias ? "#{link_alias}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}_PROTO" : "#{link_name}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}_PROTO").upcase,
value: exposed_port[:protocol]
},
{
variable: (link_alias ? "#{link_alias}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}_PORT" : "#{link_name}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}_PORT").upcase,
value: exposed_port[:hostPort]
},
{
variable: (link_alias ? "#{link_alias}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}_ADDR" : "#{link_name}_PORT_#{exposed_port[:containerPort]}_#{exposed_port[:protocol]}_ADDR").upcase,
value: "`/usr/bin/etcdctl get app/#{link_name}/#{link_name}_SERVICE_HOST`"
}
)
end
end

link_vars.map { |link| "-e #{link[:variable]}=#{link[:value]}" }
end

def environment_flags
return unless environment
environment.map { |env| "-e \"#{env[:variable]}=#{env[:value]}\"" }
environment.map { |env| "-e \'#{env[:variable]}=#{env[:value]}\'" }
end

def volume_flags
return unless volumes
volumes.map do |volume|
option = '-v '
option << "#{volume[:hostPath]}:" if volume[:hostPath].present?
option << "#{volume[:hostPath]}:" unless volume[:hostPath] == nil || volume[:hostPath] == ''
option << volume[:containerPath]
option
end
Expand Down
Loading

0 comments on commit c4830cc

Please sign in to comment.