Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 0 additions & 68 deletions lib/cloud_controller/backends/runners.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
require 'cloud_controller/diego'
require 'cloud_controller/diego/runner'
require 'cloud_controller/diego/process_guid'
require 'cloud_controller/diego/buildpack/lifecycle_protocol'
require 'cloud_controller/diego/docker/lifecycle_protocol'
require 'cloud_controller/diego/cnb/lifecycle_protocol'
require 'cloud_controller/diego/egress_rules'

module VCAP::CloudController
class Runners
Expand All @@ -13,69 +7,7 @@ def initialize(config)
end

def runner_for_process(process)
diego_runner(process)
end

def diego_processes(batch_size, last_id)
ProcessModel.select_all(ProcessModel.table_name).
diego.
runnable.
where(Sequel.lit("#{ProcessModel.table_name}.id > ?", last_id)).
order(:"#{ProcessModel.table_name}__id").
limit(batch_size).
eager(:desired_droplet, :space, :service_bindings, { routes: :domain }, { app: :buildpack_lifecycle_data }).
all
end

def processes_from_diego_process_guids(diego_process_guids)
diego_process_guids = Array(diego_process_guids).to_set
ProcessModel.select_all(ProcessModel.table_name).
diego.
runnable.
where("#{ProcessModel.table_name}__guid": diego_process_guids.map { |pg| Diego::ProcessGuid.cc_process_guid(pg) }).
order(:"#{ProcessModel.table_name}__id").
eager(:desired_droplet, :space, :service_bindings, { routes: :domain }, { app: :buildpack_lifecycle_data }).
all.
select { |process| diego_process_guids.include?(Diego::ProcessGuid.from_process(process)) }
end

def diego_apps_cache_data(batch_size, last_id)
diego_apps = ProcessModel.
diego.
runnable.
where(Sequel.lit("#{ProcessModel.table_name}.id > ?", last_id)).
order(:"#{ProcessModel.table_name}__id").
limit(batch_size)

diego_apps = diego_apps.buildpack_type unless FeatureFlag.enabled?(:diego_docker)

diego_apps.select_map([
:"#{ProcessModel.table_name}__id",
:"#{ProcessModel.table_name}__guid",
:"#{ProcessModel.table_name}__version",
:"#{ProcessModel.table_name}__updated_at"
])
end

def latest(items)
current = {}

items.each do |item|
c = current[item[:app_guid]]
current[item[:app_guid]] = item if c.nil? || (item[:created_at] == c[:created_at] && item[:id] > c[:id])
end

current
end

private

def diego_runner(process)
Diego::Runner.new(process, @config)
end

def dependency_locator
CloudController::DependencyLocator.instance
end
end
end
275 changes: 3 additions & 272 deletions spec/unit/lib/cloud_controller/backends/runners_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require 'spec_helper'
require 'cloud_controller/diego/process_guid'

module VCAP::CloudController
RSpec.describe Runners do
Expand All @@ -12,293 +11,25 @@ module VCAP::CloudController
}
})
end
let(:package_hash) { 'fake-package-hash' }
let(:buildpack) { instance_double(AutoDetectionBuildpack, custom?: false) }
let(:docker_image) { nil }

describe '#runner_for_app' do
subject(:runner) do
runners.runner_for_process(process)
end
describe '#runner_for_process' do
subject(:runner) { runners.runner_for_process(process) }

context 'when the app is configured to run on Diego' do
let(:process) { ProcessModelFactory.make(diego: true) }

it 'finds a diego backend' do
expect(runners).to receive(:diego_runner).with(process).and_call_original
it 'returns a Diego runner' do
expect(runner).to be_a(Diego::Runner)
end

context 'when the app has a docker image' do
let(:process) { ProcessModelFactory.make(:docker, docker_image: 'foobar') }

it 'finds a diego backend' do
expect(runners).to receive(:diego_runner).with(process).and_call_original
expect(runner).to be_a(Diego::Runner)
end
end
end
end

describe '#diego_apps' do
let!(:diego_process1) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process2) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process3) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process4) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process5) { ProcessModelFactory.make(diego: true, state: 'STARTED') }

it 'returns apps that have the desired data' do
last_process = ProcessModelFactory.make(diego: true, state: 'STARTED', version: 'app-version-6')

apps = runners.diego_processes(100, 0)

expect(apps.count).to eq(6)

expect(apps.last.to_json).to match_object(last_process.to_json)
end

it 'respects the batch_size' do
app_counts = [3, 5].map do |batch_size|
runners.diego_processes(batch_size, 0).count
end

expect(app_counts).to eq([3, 5])
end

it 'returns non-intersecting apps across subsequent batches' do
first_batch = runners.diego_processes(3, 0)
expect(first_batch.count).to eq(3)

second_batch = runners.diego_processes(3, first_batch.last.id)
expect(second_batch.count).to eq(2)

expect(second_batch & first_batch).to eq([])
end

it 'does not return unstaged apps' do
unstaged_process = ProcessModelFactory.make(diego: true, state: 'STARTED')
unstaged_process.desired_droplet.app.update(droplet_guid: nil)
unstaged_process.desired_droplet.destroy

batch = runners.diego_processes(100, 0)

expect(batch).not_to include(unstaged_process)
end

it "does not return apps which aren't expected to be started" do
stopped_process = ProcessModelFactory.make(diego: true, state: 'STOPPED')

batch = runners.diego_processes(100, 0)

expect(batch).not_to include(stopped_process)
end
end

describe '#diego_apps_from_process_guids' do
let!(:diego_process1) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process2) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process3) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process4) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process5) { ProcessModelFactory.make(diego: true, state: 'STARTED') }

it 'does not return unstaged apps' do
unstaged_process = ProcessModelFactory.make(diego: true, state: 'STARTED')
unstaged_process.desired_droplet.app.update(droplet_guid: nil)
unstaged_process.desired_droplet.destroy

batch = runners.processes_from_diego_process_guids(Diego::ProcessGuid.from_process(unstaged_process))

expect(batch).not_to include(unstaged_process)
end

it 'does not return apps that are stopped' do
stopped_process = ProcessModelFactory.make(diego: true, state: 'STOPPED')

batch = runners.processes_from_diego_process_guids(Diego::ProcessGuid.from_process(stopped_process))

expect(batch).not_to include(stopped_process)
end

it 'accepts a process guid or an array of process guids' do
process = ProcessModel.where(diego: true).order(:id).first
process_guid = Diego::ProcessGuid.from_process(process)

expect(runners.processes_from_diego_process_guids(process_guid)).to eq([process])
expect(runners.processes_from_diego_process_guids([process_guid])).to eq([process])
end

it 'returns diego apps for each requested process guid' do
diego_apps = ProcessModel.where(diego: true).all
diego_guids = diego_apps.map { |process| Diego::ProcessGuid.from_process(process) }

expect(runners.processes_from_diego_process_guids(diego_guids)).to match_array(diego_apps)
end

context 'when the process guid is not found' do
it 'does not return an app' do
process = ProcessModel.where(diego: true).order(:id).first
process_guid = Diego::ProcessGuid.from_process(process)

expect do
process.set_new_version
process.save
end.to change {
runners.processes_from_diego_process_guids(process_guid)
}.from([process]).to([])
end
end
end

describe '#diego_apps_cache_data' do
let!(:diego_process1) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process2) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process3) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process4) { ProcessModelFactory.make(diego: true, state: 'STARTED') }
let!(:diego_process5) { ProcessModelFactory.make(diego: true, state: 'STARTED') }

it 'respects the batch_size' do
data_count = [3, 5].map do |batch_size|
runners.diego_apps_cache_data(batch_size, 0).count
end

expect(data_count).to eq([3, 5])
end

it 'returns data for non-intersecting apps across subsequent batches' do
first_batch = runners.diego_apps_cache_data(3, 0)
expect(first_batch.count).to eq(3)

last_id = first_batch.last[0]
second_batch = runners.diego_apps_cache_data(3, last_id)
expect(second_batch.count).to eq(2)
end

it 'does not return unstaged apps' do
unstaged_process = ProcessModelFactory.make(diego: true, state: 'STARTED')
unstaged_process.desired_droplet.app.update(droplet_guid: nil)
unstaged_process.desired_droplet.destroy

batch = runners.diego_apps_cache_data(100, 0)
app_ids = batch.pluck(0)

expect(app_ids).not_to include(unstaged_process.id)
end

it 'does not return apps that are stopped' do
stopped_process = ProcessModelFactory.make(diego: true, state: 'STOPPED')

batch = runners.diego_apps_cache_data(100, 0)
app_ids = batch.pluck(0)

expect(app_ids).not_to include(stopped_process.id)
end

it 'acquires the data in one select' do
expect do
runners.diego_apps_cache_data(100, 0)
end.to have_queried_db_times(/SELECT.*FROM.*processes.*/, 1)
end

context 'with Docker app' do
before do
FeatureFlag.create(name: 'diego_docker', enabled: true)
end

let!(:docker_process) do
ProcessModelFactory.make(:docker, docker_image: 'some-image', state: 'STARTED')
end

context 'when docker is enabled' do
before do
FeatureFlag.find(name: 'diego_docker').update(enabled: true)
end

it 'returns docker apps' do
batch = runners.diego_apps_cache_data(100, 0)
app_ids = batch.pluck(0)

expect(app_ids).to include(docker_process.id)
end
end

context 'when docker is disabled' do
before do
FeatureFlag.find(name: 'diego_docker').update(enabled: false)
end

it 'does not return docker apps' do
batch = runners.diego_apps_cache_data(100, 0)
app_ids = batch.pluck(0)

expect(app_ids).not_to include(docker_process.id)
end
end
end
end

describe '#latest' do
context 'when the input hash includes a key app_guid' do
let(:input) do
[{
app_guid: 'app_guid_1',
id: 1
}]
end

it 'is added to the hash' do
output_hash = runners.latest(input)
expect(output_hash['app_guid_1']).to eq(input[0])
end

context 'when the input hash has multiple values' do
let(:input) do
[
{
app_guid: 'app_guid_1',
id: 1
},
{
app_guid: 'app_guid_2',
id: 2
}
]
end

it 'adds all items to the hash' do
output_hash = runners.latest(input)

expect(output_hash.length).to eq(input.length)
expect(output_hash['app_guid_1']).to eq(input[0])
expect(output_hash['app_guid_2']).to eq(input[1])
end
end

context 'when multiple items have the same app_guid key' do
context 'when the created_at times are the same' do
let(:time) { Time.now }

let(:input) do
[
{
app_guid: 'app_guid_1',
id: 1,
created_at: time
},
{
app_guid: 'app_guid_1',
id: 2,
created_at: time
}
]
end

it "takes the last entry based off of the 'id'" do
output_hash = runners.latest(input)
expect(output_hash['app_guid_1']).to eq(input[1])
end
end
end
end
end
end
end