Skip to content

Commit

Permalink
Add manfest support for new processes
Browse files Browse the repository at this point in the history
- If process with given type is specified in the manifest, but does not
exist yet, create it when applying manifest

[#154816221]

Signed-off-by: Tom Viehman <tviehman@pivotal.io>
  • Loading branch information
Gerg authored and tjvman committed May 3, 2018
1 parent 13f3548 commit 2a10987
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 29 deletions.
19 changes: 17 additions & 2 deletions app/actions/app_apply_manifest.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
require 'actions/process_create'
require 'actions/process_scale'
require 'actions/process_update'
require 'actions/service_binding_create'
require 'actions/manifest_route_update'
require 'cloud_controller/strategies/manifest_strategy'
Expand All @@ -16,12 +18,14 @@ def apply(app_guid, message)
app = AppModel.find(guid: app_guid)

message.manifest_process_update_messages.each do |manifest_process_update_msg|
process = ProcessModel.find(app: app, type: manifest_process_update_msg.type)
process_type = manifest_process_update_msg.type
process = find_process(app, process_type) || create_process(app, manifest_process_update_msg, process_type)

ProcessUpdate.new(@user_audit_info).update(process, manifest_process_update_msg, ManifestStrategy)
end

message.manifest_process_scale_messages.each do |manifest_process_scale_msg|
process = ProcessModel.find(app: app, type: manifest_process_scale_msg.type)
process = find_process(app, manifest_process_scale_msg.type)
ProcessScale.new(@user_audit_info, process, manifest_process_scale_msg.to_process_scale_message).scale
end

Expand All @@ -38,6 +42,17 @@ def apply(app_guid, message)

private

def find_process(app, process_type)
ProcessModel.find(app: app, type: process_type)
end

def create_process(app, manifest_process_update_msg, process_type)
ProcessCreate.new(@user_audit_info).create(app, {
type: process_type,
command: manifest_process_update_msg.command
})
end

def do_route_update(app, message)
update_message = message.manifest_routes_update_message
existing_routes = RouteMappingModel.where(app_guid: app.guid).all
Expand Down
121 changes: 94 additions & 27 deletions spec/request/app_manifests_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,43 +114,110 @@
end

describe 'multiple processes' do
let!(:process2) { VCAP::CloudController::ProcessModel.make(app: app_model, type: 'worker') }
let(:web_process) do
{
'type' => 'web',
'instances' => 4,
'command' => 'new-command',
'memory' => '2048MB',
'disk_quota' => '256MB',
'health-check-type' => 'http',
'health-check-http-endpoint' => '/test',
'timeout' => 10,
}
end

let(:worker_process) do
{
'type' => 'worker',
'instances' => 2,
'command' => 'bar',
'memory' => '512MB',
'disk_quota' => '1024M',
'health-check-type' => 'port',
'timeout' => 150
}
end

let(:yml_manifest) do
{
'applications' => [
{ 'name' => 'blah',
'processes' => [
{ 'instances' => 4, 'type' => 'web', 'command' => 'new-command', 'memory' => '2048MB', 'health_check_type' => 'http', },
{ 'instances' => 2, 'type' => 'worker', 'command' => 'bar' },
]
{
'name' => 'blah',
'processes' => [web_process, worker_process]
}
]
}.to_yaml
end

it 'applies the manifest' do
web_process = app_model.web_process
expect(web_process.instances).to eq(1)

post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)

expect(last_response.status).to eq(202)
job_guid = VCAP::CloudController::PollableJobModel.last.guid
expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))

Delayed::Worker.new.work_off
expect(VCAP::CloudController::PollableJobModel.find(guid: job_guid)).to be_complete

web_process.reload
expect(web_process.instances).to eq(4)
expect(web_process.memory).to eq(2048)
expect(web_process.command).to eq('new-command')
expect(web_process.health_check_type).to eq('http')
context 'when all the process types already exist' do
let!(:process2) { VCAP::CloudController::ProcessModel.make(app: app_model, type: 'worker') }

it 'applies the manifest' do
web_process = app_model.web_process
expect(web_process.instances).to eq(1)

post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)

expect(last_response.status).to eq(202)
job_guid = VCAP::CloudController::PollableJobModel.last.guid
expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))

Delayed::Worker.new.work_off
background_job = VCAP::CloudController::PollableJobModel.find(guid: job_guid)
expect(background_job).to be_complete, "Failed due to: #{background_job.cf_api_error}"

web_process.reload
expect(web_process.instances).to eq(4)
expect(web_process.memory).to eq(2048)
expect(web_process.disk_quota).to eq(256)
expect(web_process.command).to eq('new-command')
expect(web_process.health_check_type).to eq('http')
expect(web_process.health_check_http_endpoint).to eq('/test')
expect(web_process.health_check_timeout).to eq(10)

process2.reload
expect(process2.instances).to eq(2)
expect(process2.memory).to eq(512)
expect(process2.disk_quota).to eq(1024)
expect(process2.command).to eq('bar')
expect(process2.health_check_type).to eq('port')
expect(process2.health_check_timeout).to eq(150)
end
end

process2.reload
expect(process2.instances).to eq(2)
expect(process2.command).to eq('bar')
context 'when some of the process types do NOT exist for the app yet' do
it 'creates the processes and applies the manifest' do
web_process = app_model.web_process
expect(web_process.instances).to eq(1)

post "/v3/apps/#{app_model.guid}/actions/apply_manifest", yml_manifest, yml_headers(user_header)

expect(last_response.status).to eq(202)
job_guid = VCAP::CloudController::PollableJobModel.last.guid
expect(last_response.headers['Location']).to match(%r(/v3/jobs/#{job_guid}))

Delayed::Worker.new.work_off
background_job = VCAP::CloudController::PollableJobModel.find(guid: job_guid)
expect(background_job).to be_complete, "Failed due to: #{background_job.cf_api_error}"

web_process.reload
expect(web_process.instances).to eq(4)
expect(web_process.memory).to eq(2048)
expect(web_process.disk_quota).to eq(256)
expect(web_process.command).to eq('new-command')
expect(web_process.health_check_type).to eq('http')
expect(web_process.health_check_http_endpoint).to eq('/test')
expect(web_process.health_check_timeout).to eq(10)

process2 = VCAP::CloudController::ProcessModel.find(app_guid: app_model.guid, type: 'worker')
expect(process2.instances).to eq(2)
expect(process2.memory).to eq(512)
expect(process2.disk_quota).to eq(1024)
expect(process2.command).to eq('bar')
expect(process2.health_check_type).to eq('port')
expect(process2.health_check_timeout).to eq(150)
end
end
end
end
Expand Down
59 changes: 59 additions & 0 deletions spec/unit/actions/app_apply_manifest_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module VCAP::CloudController
let(:app_update) { instance_double(AppUpdate) }
let(:app_patch_env) { instance_double(AppPatchEnvironmentVariables) }
let(:process_update) { instance_double(ProcessUpdate) }
let(:process_create) { instance_double(ProcessCreate) }
let(:service_binding_create) { instance_double(ServiceBindingCreate) }
let(:random_route_generator) { instance_double(RandomRouteGenerator, route: 'spiffy/donut') }

Expand All @@ -21,6 +22,10 @@ module VCAP::CloudController
to receive(:new).and_return(process_scale)
allow(process_scale).to receive(:scale)

allow(ProcessCreate).
to receive(:new).and_return(process_create)
allow(process_create).to receive(:create)

allow(AppUpdate).
to receive(:new).and_return(app_update)
allow(app_update).to receive(:update)
Expand Down Expand Up @@ -320,6 +325,60 @@ module VCAP::CloudController
end
end

describe 'creating a new process' do
let(:message) do
AppManifestMessage.new({
processes: [
{ type: 'potato', command: 'potato-command', instances: 3 },
] }
)
end

let!(:app) { AppModel.make }
let(:update_message) { message.manifest_process_update_messages.first }
let(:scale_message) { message.manifest_process_scale_messages.first }

context 'when the request is valid' do
it 'returns the app' do
expect(
app_apply_manifest.apply(app.guid, message)
).to eq(app)
end

it 'calls ProcessCreate with command and type' do
app_apply_manifest.apply(app.guid, message)
expect(ProcessCreate).to have_received(:new).with(user_audit_info)
expect(process_create).to have_received(:create).with(app, { type: 'potato', command: 'potato-command' })
end

it 'updates and scales the newly created process with all the other properties' do
app_apply_manifest.apply(app.guid, message)
expect(ProcessUpdate).to have_received(:new).with(user_audit_info)
process = ProcessModel.last
expect(process_update).to have_received(:update).with(process, update_message, ManifestStrategy)

expect(ProcessScale).to have_received(:new).with(user_audit_info, process, instance_of(ProcessScaleMessage))
expect(process_scale).to have_received(:scale)
end

context 'when there is no command specified in the manifest' do
let(:message) do
AppManifestMessage.new({
processes: [
{ type: 'potato', instances: 3 },
] }
)
end

it 'sets the command to nil' do
app_apply_manifest.apply(app.guid, message)
expect(ProcessCreate).to have_received(:new).with(user_audit_info)
expect(process_create).to have_received(:create).with(app, { type: 'potato', command: nil })
end
end
end
end

describe 'updating health check type' do
let(:message) { AppManifestMessage.new({ name: 'blah', health_check_type: 'process' }) }
let(:manifest_process_update_message) { message.manifest_process_update_messages.first }
Expand Down
19 changes: 19 additions & 0 deletions spec/unit/actions/process_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ module VCAP::CloudController
expect(process.command).to eq('rackup')
end

context 'if the command is nil' do
let(:message) do
{
type: 'web',
command: nil
}
end

it 'creates the process with no command' do
process = process_create.create(app, message)

app.reload
expect(app.processes.count).to eq(1)
expect(app.processes.first.guid).to eq(process.guid)
expect(process.type).to eq('web')
expect(process.command).to eq(nil)
end
end

it 'adds existing routes to the process' do
route = Route.make(space: app.space)
RouteMappingModel.make(route: route, app: app, process_type: 'web')
Expand Down

0 comments on commit 2a10987

Please sign in to comment.