AcceptedPlan Update Process
Implementation can be found naturally in the server:
limits/server/v1/accepted_plan/accepted_plan_service.go
. We pass
of course “actual” creation to the core server, but this is just
a small step, the whole logic to execute before any CUD operation
is much more significant here.
When the server processes the AcceptedPlan resource (Create or Update), then we are guaranteed to be in the Limits service region where the assigner resides. Because LimitsPools are a child of Service or Organization, we can guarantee that they reside on the same regional database as AcceptedPlan. Thanks to this, we can verify, within the SNAPSHOT transaction, that the caller does not attempt to create/update any AcceptedPlan that would exceed the limit pools of an assigner! This is the primary guarantee here: Assigner will not be able to exceed allocated values in its LimitPools. We need to check cases where AcceptedPlan increases reservations on Assigner LimitPools. When we decrease (some updates, deletions), then we don’t need to do that.
However, there is some risk with decreasing accepted plans
(some updates and deletions). There is a risk that doing so would
decrease assignee limit values below current usage. To prevent this,
in the function validateAssigneeLimitsAndGetLimitPoolUpdates
in
server implementation, we are checking assignee limit values. This
will work in 99.99% of cases unless some new resources will be
allocated while we confirm that we can decrease limits. Therefore,
we don’t have guarantees here.
In the result, when we create/update AcceptedPlan, we are only increasing LimitPools reservations values of Assigner. When we would decrease LimitPool values, we just don’t yet.
Decreasing values is done by the Limits controller, we have a task for this,
in limits/controller/v1/limits_assigner/limit_pool_state_syncer.go
.
It takes into account all child Limit and LimitPool instances (for assignees),
which are synchronized with PlanAssignment instances. It then sends
UpdateLimitPool requests when it confirms decreased values of AcceptedPlan
action (updated or deleted) took an effect. Reservation is immediate,
release is asynchronous and delayed.
Some cheating however is potentially possible, if the org admin sends UpdateLimitPool trying to minimize the “Reserved” field, after which it can attempt to create a new accepted plan quickly enough before the controller fixes values again. Securing this may be a bit more tricky, but such an update would leave LimitPool with a Reserved value way above the configured size, which will be detectable, along with ActivityLogs, and if not, ResourceChangeLogs. It is unlikely it will be tried this way. A potential way to secure this would be to disable AcceptedPlan updates if the Reserved value of LimitPool decreased recently, with some timeout like 30 seconds. Optionally, we can just put some custom code in the API Server for UpdateLimitPool, and validate only straight service admin updates them (check principal from context). This is not covered by IAM Authorization code-gen middleware, but custom code can simply do.