Skip to content

Commit

Permalink
Refactor Application into a module
Browse files Browse the repository at this point in the history
- Depends on Stage for API access
- Refactored inner loop
- Used #with_app macro in testing
  • Loading branch information
kmayer committed Jul 29, 2013
1 parent 5a01448 commit 449cdf8
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 157 deletions.
2 changes: 2 additions & 0 deletions lib/heroku_san/api.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'time'

module HerokuSan
class API
def initialize(options = {})
Expand Down
52 changes: 19 additions & 33 deletions lib/heroku_san/application.rb
Original file line number Diff line number Diff line change
@@ -1,50 +1,36 @@
require 'heroku-api'

MOCK = false unless defined?(MOCK)

module HerokuSan
class Application
def initialize app_name
@app_name = app_name
@api = HerokuSan::API.new(:api_key => auth_token, :mock => MOCK)
end

def ensure_one_worker_running
one_up = false
until one_up do
processes = @api.get_ps(@app_name).body
web_processes = processes.select { |p| p["process"] =~ /web\./ }

web_processes.each do |process|
case process["state"]
when "up"
one_up = true
when "crashed"
@api.post_ps_restart(@app_name, ps: process["process"])
end
end
end
module Application
def ensure_one_worker_running(at_least = 1)
begin
web_processes = heroku.get_ps(app).body.select { |p| p["process"] =~ /web\./ }
end until restart_processes(web_processes) >= at_least
end

def ensure_all_workers_running
while true do
processes = @api.get_ps(@app_name).body
processes = heroku.get_ps(app).body

return if processes.all? {|p| p["state"] == "up"}
return if processes.all? { |p| p["state"] == "up" }

processes.each do |process|
case process["state"]
when "crashed"
@api.post_ps_restart(@app_name, ps: process["process"])
end
end
restart_processes(processes)
end
end

private

def auth_token
@auth_token ||= (ENV['HEROKU_API_KEY'] || `heroku auth:token`.chomp unless MOCK)
def restart_processes(web_processes)
up = 0
web_processes.each do |process|
case process["state"]
when "up"
up += 1
when "crashed"
heroku.post_ps_restart(app, ps: process["process"])
end
end
up
end
end
end
2 changes: 2 additions & 0 deletions lib/heroku_san/stage.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'heroku-api'
require 'json'
require_relative 'application'

MOCK = false unless defined?(MOCK)

Expand All @@ -8,6 +9,7 @@ class Stage
include HerokuSan::Git
attr_reader :name
attr_reader :options
include HerokuSan::Application

def initialize(stage, options = {})
@name = stage
Expand Down
188 changes: 64 additions & 124 deletions spec/heroku_san/application_spec.rb
Original file line number Diff line number Diff line change
@@ -1,141 +1,81 @@
require 'spec_helper'
require_relative '../../lib/heroku_san/application.rb'

module HerokuSan
describe HerokuSan::Application do
let(:application) { HerokuSan::Application.new(app_name)}
let(:app_name) { stub }
let(:api) { stub }

before do
HerokuSan::API.stub(:new) { api }
end

describe "#ensure_one_worker_running" do
describe HerokuSan::Application do
let(:stage) { HerokuSan::Stage.new('production', {"deploy" => HerokuSan::Deploy::Rails, "app" => "awesomeapp", "stack" => "cedar"}) }
let(:response) { stub }
let(:none_running) do
[
{
"process" => "worker.1",
"state" => "up"
},
{
"process" => "web.1",
"state" => "starting"
},
{
"process" => "web.2",
"state" => "starting"
}
]
end
let(:one_running_with_crash) do
[
{
"process" => "worker.1",
"state" => "up"
},
{
"process" => "web.1",
"state" => "crashed"
},
{
"process" => "web.2",
"state" => "up"
}
]
end

before do
api.stub(:get_ps).with(app_name) { response }
stage.heroku.stub(:get_ps).with(stage.app) { response }
end

it "should block until at least one worker is running, and restart any crashed workers it sees" do
response.should_receive(:body).twice.and_return(none_running, one_running_with_crash)
api.should_receive(:post_ps_restart).with(app_name, ps: 'web.1')
describe "#ensure_one_worker_running" do
# API: {"action" => "up", "app_name" => "awesomeapp", "attached" => false, "command" => "thin -p $PORT -e $RACK_ENV -R $HEROKU_RACK start", "elapsed" => 0, "pretty_state" => "created for 0s", "process" => "web.1", "rendezvous_url" => nil, "slug" => "NONE", "state" => "created", "transitioned_at" => "2013/07/28 15:04:24 -0700", "type" => "Dyno", "upid" => "56003928"}
let(:none_running) do
[
{"process" => "worker.1", "state" => "up"},
{"process" => "web.1", "state" => "starting"},
{"process" => "web.2", "state" => "starting"}
]
end
let(:one_running_with_crash) do
[
{"process" => "worker.1", "state" => "up"},
{"process" => "web.1", "state" => "crashed"},
{"process" => "web.2", "state" => "up"}
]
end

application.ensure_one_worker_running
end
end
it "should block until at least one worker is running, and restart any crashed workers it sees" do

describe "#ensure_all_workers_running" do
let(:response) { stub }
let(:some_crashes) do
[
{
"process" => "worker.1",
"state" => "crashed"
},
{
"process" => "web.1",
"state" => "crashed"
},
{
"process" => "web.2",
"state" => "starting"
}
]
end
let(:some_restarting) do
[
{
"process" => "worker.1",
"state" => "restarting"
},
{
"process" => "web.1",
"state" => "restarting"
},
{
"process" => "web.2",
"state" => "up"
}
]
end
let(:one_crash) do
[
{
"process" => "worker.1",
"state" => "crashed"
},
{
"process" => "web.1",
"state" => "up"
},
{
"process" => "web.2",
"state" => "up"
}
]
end
let(:all_up) do
[
{
"process" => "worker.1",
"state" => "up"
},
{
"process" => "web.1",
"state" => "up"
},
{
"process" => "web.2",
"state" => "up"
}
]
end
with_app(stage, 'name' => stage.app) do |app_data|
response.should_receive(:body).twice.and_return(none_running, one_running_with_crash)
stage.heroku.should_receive(:post_ps_restart).with(stage.app, ps: 'web.1')

before do
api.stub(:get_ps).with(app_name) { response }
stage.ensure_one_worker_running
end
end
end

it "should block until all workers are running, and restart any crashed workers it sees" do
response.should_receive(:body).exactly(4).times.and_return(some_crashes, some_restarting, one_crash, all_up)
api.should_receive(:post_ps_restart).with(app_name, ps: 'worker.1').twice
api.should_receive(:post_ps_restart).with(app_name, ps: 'web.1').once
describe "#ensure_all_workers_running" do
let(:some_crashes) do
[
{"process" => "worker.1", "state" => "crashed"},
{"process" => "web.1", "state" => "crashed"},
{"process" => "web.2", "state" => "starting"}
]
end
let(:some_restarting) do
[
{"process" => "worker.1", "state" => "restarting"},
{"process" => "web.1", "state" => "restarting"},
{"process" => "web.2", "state" => "up"}
]
end
let(:one_crash) do
[
{"process" => "worker.1", "state" => "crashed"},
{"process" => "web.1", "state" => "up"},
{"process" => "web.2", "state" => "up"}
]
end
let(:all_up) do
[
{"process" => "worker.1", "state" => "up"},
{"process" => "web.1", "state" => "up"},
{"process" => "web.2", "state" => "up"}
]
end

it "should block until all workers are running, and restart any crashed workers it sees" do
with_app(stage, 'name' => stage.app) do |app_data|
response.should_receive(:body).exactly(4).times.and_return(some_crashes, some_restarting, one_crash, all_up)
stage.heroku.should_receive(:post_ps_restart).with(stage.app, ps: 'worker.1').twice
stage.heroku.should_receive(:post_ps_restart).with(stage.app, ps: 'web.1').once

application.ensure_all_workers_running
stage.ensure_all_workers_running
end
end
end
end
end
end

0 comments on commit 449cdf8

Please sign in to comment.