Skip to content

Commit d4b24db

Browse files
committed
fix(aws-marketplace): confirm a container license lapse on a second re-check before switching Enterprise off
1 parent 9c3aab4 commit d4b24db

2 files changed

Lines changed: 34 additions & 5 deletions

File tree

‎apps/sim/lib/billing/aws-marketplace/container-license.test.ts‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,33 @@ describe('applyAwsMarketplaceContainerLicense', () => {
9595
})
9696

9797
it.each<Answer>(['NoEntitlementsAllowedException', 'ResourceNotFoundException'])(
98-
'restarts without Enterprise once the license ends (%s)',
98+
'restarts without Enterprise once a second re-check confirms the license ended (%s)',
9999
async (answer) => {
100100
answers = ['entitled', answer]
101101
await applyAwsMarketplaceContainerLicense()
102102
await vi.advanceTimersByTimeAsync(16 * MINUTE)
103+
expect(kill).not.toHaveBeenCalled()
104+
await vi.advanceTimersByTimeAsync(16 * MINUTE)
103105
expect(kill).toHaveBeenCalledWith(process.pid, 'SIGTERM')
104106
}
105107
)
106108

109+
it.each<Answer>(['entitled', 'ServerInternalException'])(
110+
'keeps Enterprise on when a lone not-entitled answer is followed by %s',
111+
async (answer) => {
112+
answers = [
113+
'entitled',
114+
'NoEntitlementsAllowedException',
115+
answer,
116+
'NoEntitlementsAllowedException',
117+
'entitled',
118+
]
119+
await applyAwsMarketplaceContainerLicense()
120+
await vi.advanceTimersByTimeAsync(80 * MINUTE)
121+
expect(kill).not.toHaveBeenCalled()
122+
}
123+
)
124+
107125
it('restarts into Enterprise once the license becomes active', async () => {
108126
answers = ['NoEntitlementsAllowedException', 'entitled']
109127
await applyAwsMarketplaceContainerLicense()

‎apps/sim/lib/billing/aws-marketplace/container-license.ts‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ import { awsRequestHandler } from '@/lib/core/network/proxy-agent.server'
1919
*
2020
* Opt-in: nothing here runs unless {@link PRODUCT_SKU_ENV} is set, which only the Marketplace
2121
* Helm profile does. Enterprise flags are read once at startup by both the server and the
22-
* browser, so instead of flipping them live, a re-check that sees the answer change asks the
23-
* process to shut down and the orchestrator starts it again in the right mode.
22+
* browser, so instead of flipping them live, a re-check that sees the answer change (a downgrade
23+
* once confirmed) asks the process to shut down and the orchestrator starts it again in the right
24+
* mode.
2425
*/
2526

2627
const logger = createLogger('AwsMarketplaceContainerLicense')
@@ -52,6 +53,13 @@ const RECHECK_JITTER_MS = 2 * 60 * 1000
5253
*/
5354
const RESTART_SPREAD_MS = 10 * 60 * 1000
5455

56+
/**
57+
* Consecutive "not entitled" re-checks a running Enterprise deployment needs before it switches
58+
* Enterprise off, so one spurious answer never restarts a paying customer's replicas. A real lapse
59+
* takes effect one re-check later.
60+
*/
61+
const DOWNGRADE_CONFIRMATIONS = 2
62+
5563
const ENTERPRISE_FLAGS = ['ENTERPRISE_ENABLED', 'NEXT_PUBLIC_ENTERPRISE_ENABLED'] as const
5664

5765
/** License Manager's answers when the account holds no active license with the entitlement. */
@@ -157,11 +165,14 @@ export async function applyAwsMarketplaceContainerLicense(): Promise<void> {
157165
const scheduleRecheck = () => {
158166
setTimeout(recheck, RECHECK_INTERVAL_MS + randomInt(0, RECHECK_JITTER_MS)).unref()
159167
}
168+
let consecutiveNotEntitled = 0
160169
const recheck = async () => {
161170
const state = await checkLicense(client, productSku)
162-
// A failed check keeps the current mode; only a definite answer that differs restarts.
171+
// A failed check keeps the current mode.
172+
consecutiveNotEntitled = state === 'not-entitled' ? consecutiveNotEntitled + 1 : 0
163173
const changed =
164-
(state === 'entitled' && !enterpriseAtBoot) || (state === 'not-entitled' && enterpriseAtBoot)
174+
(state === 'entitled' && !enterpriseAtBoot) ||
175+
(enterpriseAtBoot && consecutiveNotEntitled >= DOWNGRADE_CONFIRMATIONS)
165176
if (changed) return restartToApply(state)
166177
scheduleRecheck()
167178
}

0 commit comments

Comments
 (0)