Skip to content

Commit

Permalink
add process readiness changed controller and event (cloudfoundry#3554)
Browse files Browse the repository at this point in the history
Signed-off-by: Geoff Franks <[email protected]>
Co-authored-by: Geoff Franks <[email protected]>
  • Loading branch information
ameowlia and geofffranks authored Jan 11, 2024
1 parent dd95c36 commit 06ca46a
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 0 deletions.
38 changes: 38 additions & 0 deletions app/controllers/internal/app_readiness_changed_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'sinatra'
require 'controllers/base/base_controller'

module VCAP::CloudController
class AppReadinessChangedController < RestController::BaseController
# Endpoint does its own (non-standard) auth
allow_unauthenticated_access

post '/internal/v4/apps/:process_guid/readiness_changed', :readiness_changed
def readiness_changed(process_guid)
payload = readiness_request

app_guid = Diego::ProcessGuid.cc_process_guid(process_guid)

process = ProcessModel.find(guid: app_guid)
raise CloudController::Errors::NotFound.new_from_details('ProcessNotFound', app_guid) unless process

payload['version'] = Diego::ProcessGuid.cc_process_version(process_guid)

Repositories::ProcessEventRepository.record_readiness_changed(process, payload)
end

private

def readiness_request
readiness = {}
begin
payload = body.read
readiness = MultiJson.load(payload)
rescue MultiJson::ParseError => e
logger.error('diego.app_readiness_changed.parse-error', payload: payload, error: e.to_s)
raise CloudController::Errors::ApiError.new_from_details('MessageParseError', payload)
end

readiness
end
end
end
2 changes: 2 additions & 0 deletions app/repositories/event_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class EventTypesError < StandardError
APP_PROCESS_CRASH = 'audit.app.process.crash'.freeze,
APP_PROCESS_TERMINATE_INSTANCE = 'audit.app.process.terminate_instance'.freeze,
APP_PROCESS_SCALE = 'audit.app.process.scale'.freeze,
APP_PROCESS_READY = 'audit.app.process.ready'.freeze,
APP_PROCESS_NOT_READY = 'audit.app.process.not-ready'.freeze,

APP_DROPLET_CREATE = 'audit.app.droplet.create'.freeze,
APP_DROPLET_UPLOAD = 'audit.app.droplet.upload'.freeze,
Expand Down
19 changes: 19 additions & 0 deletions app/repositories/process_event_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,25 @@ def self.record_crash(process, crash_payload)
)
end

def self.record_readiness_changed(process, readiness_changed_payload)
if readiness_changed_payload['ready']
VCAP::AppLogEmitter.emit(process.guid, "Process became ready with guid #{process.guid} payload: #{readiness_changed_payload}")
type = EventTypes::APP_PROCESS_READY
else
VCAP::AppLogEmitter.emit(process.guid, "Process became not ready with guid #{process.guid} payload: #{readiness_changed_payload}")
type = EventTypes::APP_PROCESS_NOT_READY
end

create_event(
process: process,
type: type,
actor_guid: process.guid,
actor_name: process.type,
actor_type: 'process',
metadata: readiness_changed_payload
)
end

def self.record_rescheduling(process, rescheduling_payload)
VCAP::AppLogEmitter.emit(process.app_guid, 'Process is being rescheduled')

Expand Down
2 changes: 2 additions & 0 deletions docs/v3/source/includes/resources/audit_events/_header.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ For more information, see the [Cloud Foundry docs](https://docs.cloudfoundry.org
- `audit.app.process.crash`
- `audit.app.process.create`
- `audit.app.process.delete`
- `audit.app.process.ready`
- `audit.app.process.not-ready`
- `audit.app.process.rescheduling`
- `audit.app.process.scale`
- `audit.app.process.terminate_instance`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
require 'spec_helper'

## NOTICE: Prefer request specs over controller specs as per ADR #0003 ##

module VCAP::CloudController
RSpec.describe AppReadinessChangedController do
describe 'POST /internal/v4/apps/:process_guid/readiness_changed' do
let(:diego_process) { ProcessModelFactory.make(state: 'STARTED', diego: true) }
let(:process_guid) { Diego::ProcessGuid.from(diego_process.guid, 'some-version-guid') }
let(:url) { "/internal/v4/apps/#{process_guid}/readiness_changed" }
let(:ready) { true }

let(:readiness_changed_request) do
{
'instance' => Sham.guid,
'index' => 3,
'ready' => ready
}
end

describe 'validation' do
context 'when sending invalid json' do
it 'fails with a 400' do
post url, 'this is not json'

expect(last_response.status).to eq(400)
expect(last_response.body).to match(/MessageParseError/)
end
end
end

context 'when the app is ready' do
it 'audits the app readiness changed event' do
post url, MultiJson.dump(readiness_changed_request)
expect(last_response.status).to eq(200)

app_event = Event.find(actee: diego_process.guid, actor_type: 'process')

expect(app_event).to be
expect(app_event.space).to eq(diego_process.space)
expect(app_event.type).to eq('audit.app.process.ready')
expect(app_event.actor_type).to eq('process')
expect(app_event.actor).to eq(diego_process.guid)
expect(app_event.metadata['instance']).to eq(readiness_changed_request['instance'])
expect(app_event.metadata['index']).to eq(readiness_changed_request['index'])
end
end

context 'when the app is not ready' do
let(:ready) { false }

it 'audits the app readiness changed event' do
post url, MultiJson.dump(readiness_changed_request)
expect(last_response.status).to eq(200)

app_event = Event.find(actee: diego_process.guid, actor_type: 'process')

expect(app_event).to be
expect(app_event.space).to eq(diego_process.space)
expect(app_event.type).to eq('audit.app.process.not-ready')
expect(app_event.actor_type).to eq('process')
expect(app_event.actor).to eq(diego_process.guid)
expect(app_event.metadata['instance']).to eq(readiness_changed_request['instance'])
expect(app_event.metadata['index']).to eq(readiness_changed_request['index'])
end
end

context 'when the app no longer exists' do
before { diego_process.delete }

it 'fails with a 404' do
post url, MultiJson.dump(readiness_changed_request)

expect(last_response.status).to eq(404)
expect(last_response.body).to match(/ProcessNotFound/)
end
end
end
end
end
2 changes: 2 additions & 0 deletions spec/unit/repositories/event_types_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ module Repositories
'audit.app.process.create',
'audit.app.process.update',
'audit.app.process.delete',
'audit.app.process.ready',
'audit.app.process.not-ready',
'audit.app.process.rescheduling',
'audit.app.process.crash',
'audit.app.process.terminate_instance',
Expand Down
58 changes: 58 additions & 0 deletions spec/unit/repositories/process_event_repository_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,64 @@ module Repositories
end
end

describe '.record_readiness_changed' do
let(:ready) { true }
let(:readiness_payload) do
{
'instance' => 'abc',
'index' => 3,
'cell_id' => 'some-cell',
'ready' => ready
}
end

context 'when the process is ready' do
it 'creates a new audit.app.process.ready event' do
event = ProcessEventRepository.record_readiness_changed(process, readiness_payload)
event.reload

expect(event.type).to eq('audit.app.process.ready')
expect(event.actor).to eq(process.guid)
expect(event.actor_type).to eq('process')
expect(event.actor_name).to eq('potato')
expect(event.actor_username).to eq('')
expect(event.actee).to eq(app.guid)
expect(event.actee_type).to eq('app')
expect(event.actee_name).to eq('zach-loves-kittens')
expect(event.space_guid).to eq(app.space.guid)
expect(event.organization_guid).to eq(app.space.organization.guid)

expect(event.metadata['instance']).to eq('abc')
expect(event.metadata['index']).to eq(3)
expect(event.metadata['cell_id']).to eq('some-cell')
end
end

context 'when the process is not ready' do
let(:ready) { false }

it 'creates a new audit.app.process.not-ready event' do
event = ProcessEventRepository.record_readiness_changed(process, readiness_payload)
event.reload

expect(event.type).to eq('audit.app.process.not-ready')
expect(event.actor).to eq(process.guid)
expect(event.actor_type).to eq('process')
expect(event.actor_name).to eq('potato')
expect(event.actor_username).to eq('')
expect(event.actee).to eq(app.guid)
expect(event.actee_type).to eq('app')
expect(event.actee_name).to eq('zach-loves-kittens')
expect(event.space_guid).to eq(app.space.guid)
expect(event.organization_guid).to eq(app.space.organization.guid)

expect(event.metadata['instance']).to eq('abc')
expect(event.metadata['index']).to eq(3)
expect(event.metadata['cell_id']).to eq('some-cell')
end
end
end

describe '.record_rescheduling' do
let(:rescheduling_payload) do
{
Expand Down

0 comments on commit 06ca46a

Please sign in to comment.