Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add process readiness changed controller and event #3554

Merged
merged 1 commit into from
Jan 11, 2024
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
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