Skip to content

Commit 568bb0b

Browse files
committed
Add automatic Diego sync callbacks to RouteAccessRule
- Add after_create and after_destroy callbacks to touch associated processes - Updates process.updated_at to trigger Diego ProcessesSync immediately - Eliminates 30-second wait for access rule changes to propagate to GoRouter - Add comprehensive unit tests for callbacks and validations - Ensure RouteAccessRule model is loaded in app/models.rb This enables automatic synchronization of access rules to Diego/GoRouter within seconds instead of requiring manual app restarts or waiting for the next sync cycle.
1 parent 67f7862 commit 568bb0b

3 files changed

Lines changed: 135 additions & 0 deletions

File tree

app/models.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
require 'models/runtime/revision_sidecar_model'
7070
require 'models/runtime/revision_sidecar_process_type_model'
7171
require 'models/runtime/route'
72+
require 'models/runtime/route_access_rule'
7273
require 'models/runtime/space_routes'
7374
require 'models/runtime/space_quota_definition'
7475
require 'models/runtime/stack'

app/models/runtime/route_access_rule.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,27 @@ def validate
1111
validates_presence :selector
1212
validates_presence :route_id
1313
end
14+
15+
def after_create
16+
super
17+
touch_associated_processes
18+
end
19+
20+
def after_destroy
21+
super
22+
touch_associated_processes
23+
end
24+
25+
private
26+
27+
def touch_associated_processes
28+
# Update the timestamp on all processes associated with this route
29+
# This triggers Diego's ProcessesSync to pick up the route changes
30+
return unless route
31+
32+
route.apps.each do |process|
33+
process.update(updated_at: Time.now)
34+
end
35+
end
1436
end
1537
end
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
require 'spec_helper'
2+
3+
module VCAP::CloudController
4+
RSpec.describe RouteAccessRule, type: :model do
5+
let(:space) { Space.make }
6+
let(:domain) { SharedDomain.make(name: 'apps.identity') }
7+
let(:route) { Route.make(space: space, domain: domain) }
8+
let(:process) { ProcessModelFactory.make(space: space) }
9+
let(:app_guid) { SecureRandom.uuid }
10+
11+
before do
12+
RouteMappingModel.make(app: process, route: route, process_type: 'web')
13+
end
14+
15+
describe 'validations' do
16+
it 'requires a name' do
17+
rule = RouteAccessRule.new(selector: 'cf:app:123', route: route)
18+
expect(rule.valid?).to be false
19+
expect(rule.errors[:name]).to include("can't be blank")
20+
end
21+
22+
it 'requires a selector' do
23+
rule = RouteAccessRule.new(name: 'test-rule', route: route)
24+
expect(rule.valid?).to be false
25+
expect(rule.errors[:selector]).to include("can't be blank")
26+
end
27+
28+
it 'requires a route_id' do
29+
rule = RouteAccessRule.new(name: 'test-rule', selector: 'cf:app:123')
30+
expect(rule.valid?).to be false
31+
expect(rule.errors[:route_id]).to include("can't be blank")
32+
end
33+
end
34+
35+
describe 'associations' do
36+
it 'belongs to a route' do
37+
rule = RouteAccessRule.create(
38+
name: 'test-rule',
39+
selector: 'cf:app:123',
40+
route: route
41+
)
42+
expect(rule.route).to eq(route)
43+
end
44+
end
45+
46+
describe 'callbacks' do
47+
describe 'after_create' do
48+
it 'touches associated processes to trigger Diego sync' do
49+
initial_updated_at = process.updated_at
50+
51+
# Sleep to ensure timestamp difference
52+
sleep 0.1
53+
54+
RouteAccessRule.create(
55+
name: 'test-rule',
56+
selector: "cf:app:#{app_guid}",
57+
route: route
58+
)
59+
60+
process.reload
61+
expect(process.updated_at).to be > initial_updated_at
62+
end
63+
64+
it 'does not fail if route has no associated processes' do
65+
route_without_processes = Route.make(space: space, domain: domain)
66+
67+
expect {
68+
RouteAccessRule.create(
69+
name: 'test-rule',
70+
selector: "cf:app:#{app_guid}",
71+
route: route_without_processes
72+
)
73+
}.not_to raise_error
74+
end
75+
end
76+
77+
describe 'after_destroy' do
78+
it 'touches associated processes to trigger Diego sync' do
79+
rule = RouteAccessRule.create(
80+
name: 'test-rule',
81+
selector: "cf:app:#{app_guid}",
82+
route: route
83+
)
84+
85+
process.reload
86+
initial_updated_at = process.updated_at
87+
88+
# Sleep to ensure timestamp difference
89+
sleep 0.1
90+
91+
rule.destroy
92+
93+
process.reload
94+
expect(process.updated_at).to be > initial_updated_at
95+
end
96+
97+
it 'does not fail if route has no associated processes' do
98+
route_without_processes = Route.make(space: space, domain: domain)
99+
rule = RouteAccessRule.create(
100+
name: 'test-rule',
101+
selector: "cf:app:#{app_guid}",
102+
route: route_without_processes
103+
)
104+
105+
expect {
106+
rule.destroy
107+
}.not_to raise_error
108+
end
109+
end
110+
end
111+
end
112+
end

0 commit comments

Comments
 (0)