ce7cb702218693d08ea6d83464d2457c9250027c
This commit is contained in:
commit
34c5f8963c
22 changed files with 145336 additions and 0 deletions
333
docs/spec/tier-s/Cloud.tla
Normal file
333
docs/spec/tier-s/Cloud.tla
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
------------------------------- MODULE Cloud -------------------------------
|
||||
EXTENDS Naturals, FiniteSets, TLC
|
||||
|
||||
CONSTANTS Mode, Scenario, ResourceKind, LoseReply, RestartOnce, DelayLists
|
||||
|
||||
(* This projection separates OCI resource state, API observations, and publication. *)
|
||||
(* cloud.Client.Reconcile supplies the create/select/cleanup protocol for each kind. *)
|
||||
|
||||
Kinds == {"CPE", "VPN"}
|
||||
Modes == {"Current", "Fixed"}
|
||||
Cases == {"Retry", "Routes"}
|
||||
Ids == 1..3
|
||||
WANs == {"A", "B"}
|
||||
VIPs == {"X", "Y"}
|
||||
Attempts == 0..2
|
||||
Keys == WANs \X Attempts
|
||||
Lives == {"Unused", "Provisioning", "Available", "Terminating", "Terminated", "Absent"}
|
||||
Phases == {"read", "create", "reply", "inspect", "clean", "admit", "publish", "pause"}
|
||||
|
||||
ASSUME /\ Mode \in Modes
|
||||
/\ Scenario \in Cases
|
||||
/\ ResourceKind \in Kinds
|
||||
/\ Scenario = "Routes" => ResourceKind = "VPN"
|
||||
/\ LoseReply \in BOOLEAN
|
||||
/\ RestartOnce \in BOOLEAN
|
||||
/\ DelayLists \in BOOLEAN
|
||||
|
||||
VARIABLES objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
phase, seen, selected, attempt, reply, routeSeen, pending,
|
||||
health, published, admitted, todo, refs, dropped, restarted, badPublish
|
||||
|
||||
vars == <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
phase, seen, selected, attempt, reply, routeSeen, pending,
|
||||
health, published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
LastEpoch == IF Scenario = "Retry" THEN 2 ELSE 1
|
||||
WAN == IF Scenario = "Retry" /\ epoch = 1 THEN "B" ELSE "A"
|
||||
VIP == IF Scenario = "Routes" /\ epoch = 1 THEN "Y" ELSE "X"
|
||||
Key == <<WAN, attempt>>
|
||||
Active == {i \in Ids : objects[i].life \in {"Provisioning", "Available"}}
|
||||
Present == {i \in Ids : objects[i].life \notin {"Unused", "Absent"}}
|
||||
Current == {i \in seen : objects[i].wan = WAN /\
|
||||
(ResourceKind = "CPE" \/
|
||||
objects[i].life \notin {"Terminating", "Terminated", "Absent"})}
|
||||
Old == {i \in seen : i # selected}
|
||||
|
||||
Init == /\ objects = [i \in Ids |-> [life |-> "Unused", wan |-> "A", route |-> "X"]]
|
||||
/\ visible = {}
|
||||
/\ tokens = [k \in Keys |-> 0]
|
||||
/\ invalid = {}
|
||||
/\ issued = {}
|
||||
/\ nextId = 1
|
||||
/\ epoch = 0
|
||||
/\ phase = "read"
|
||||
/\ seen = {}
|
||||
/\ selected = 0
|
||||
/\ attempt = 0
|
||||
/\ reply = 0
|
||||
/\ routeSeen = "X"
|
||||
/\ pending = [i \in Ids |-> "None"]
|
||||
/\ health = {}
|
||||
/\ published = [epoch |-> 3, id |-> 0, route |-> "X"]
|
||||
/\ admitted = {}
|
||||
/\ todo = {}
|
||||
/\ refs = {}
|
||||
/\ dropped = FALSE
|
||||
/\ restarted = FALSE
|
||||
/\ badPublish = FALSE
|
||||
|
||||
(* cloud.Client.Reconcile lists before selecting a CPE or a non-terminating VPN. *)
|
||||
Read == /\ phase = "read"
|
||||
/\ seen' = visible
|
||||
/\ phase' = "inspect"
|
||||
/\ selected' = 0
|
||||
/\ attempt' = 0
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
reply, routeSeen, pending, health, published, admitted, todo,
|
||||
refs, dropped, restarted, badPublish>>
|
||||
|
||||
Select == /\ phase = "inspect" /\ selected = 0
|
||||
/\ IF Cardinality(Current) = 1
|
||||
THEN /\ selected' = CHOOSE i \in Current : TRUE
|
||||
/\ routeSeen' = objects[selected'].route
|
||||
/\ phase' = "inspect"
|
||||
ELSE /\ selected' = 0
|
||||
/\ UNCHANGED routeSeen
|
||||
/\ phase' = IF Current = {} THEN "create" ELSE "pause"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, attempt, reply, pending, health, published, admitted,
|
||||
todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
(* OCI Retry Token and InvalidatedRetryToken define these three create outcomes. *)
|
||||
(* https://docs.oracle.com/en-us/iaas/Content/API/References/apierrors.htm *)
|
||||
Create == /\ phase = "create"
|
||||
/\ Key \notin invalid
|
||||
/\ tokens[Key] = 0
|
||||
/\ nextId \in Ids
|
||||
/\ objects' = [objects EXCEPT ![nextId] =
|
||||
[life |-> IF ResourceKind = "VPN" THEN "Provisioning" ELSE "Available",
|
||||
wan |-> WAN, route |-> VIP]]
|
||||
/\ tokens' = [tokens EXCEPT ![Key] = nextId]
|
||||
/\ issued' = issued \cup {<<Key, nextId>>}
|
||||
/\ reply' = nextId
|
||||
/\ nextId' = nextId + 1
|
||||
/\ visible' = IF DelayLists THEN visible ELSE visible \cup {nextId}
|
||||
/\ phase' = "reply"
|
||||
/\ UNCHANGED <<invalid, epoch, seen, selected, attempt, routeSeen, pending,
|
||||
health, published, admitted, todo, refs, dropped,
|
||||
restarted, badPublish>>
|
||||
|
||||
Replay == /\ phase = "create" /\ Key \notin invalid /\ tokens[Key] # 0
|
||||
/\ reply' = tokens[Key]
|
||||
/\ phase' = "reply"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
(* Fixed follows a deterministic successor only on InvalidatedRetryToken; restart replays that chain. *)
|
||||
Reject == /\ phase = "create" /\ Key \in invalid
|
||||
/\ IF Mode = "Fixed" /\ attempt < 2
|
||||
THEN /\ attempt' = attempt + 1 /\ phase' = "create"
|
||||
ELSE /\ UNCHANGED attempt /\ phase' = "pause"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, reply, routeSeen, pending, health, published,
|
||||
admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
Receive == /\ phase = "reply"
|
||||
/\ selected' = reply
|
||||
/\ routeSeen' = objects[reply].route
|
||||
/\ seen' = seen \cup {reply}
|
||||
/\ phase' = "inspect"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
attempt, reply, pending, health, published, admitted, todo,
|
||||
refs, dropped, restarted, badPublish>>
|
||||
|
||||
(* A timed-out create may already have committed; only its response is lost. *)
|
||||
DropReply == /\ LoseReply /\ ~dropped /\ phase = "reply"
|
||||
/\ dropped' = TRUE
|
||||
/\ phase' = "read"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, restarted, badPublish>>
|
||||
|
||||
Restart == /\ RestartOnce /\ ~restarted
|
||||
/\ restarted' = TRUE
|
||||
/\ phase' = "read"
|
||||
/\ selected' = 0
|
||||
/\ attempt' = 0
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, reply, routeSeen, pending, health, published, admitted,
|
||||
todo, refs, dropped, badPublish>>
|
||||
|
||||
(* UpdateIPSecConnectionDetails.StaticRoutes replaces the managed route set. *)
|
||||
UpdateRoute == /\ phase = "inspect" /\ selected # 0
|
||||
/\ objects[selected].life = "Available"
|
||||
/\ ResourceKind = "VPN" /\ Mode = "Fixed" /\ routeSeen # VIP
|
||||
/\ pending' = [pending EXCEPT ![selected] = VIP]
|
||||
/\ phase' = "pause"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId,
|
||||
epoch, seen, selected, attempt, reply, routeSeen, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
(* CPE unavailability here represents a rejected child-IPsec call, not a readable CPE lifecycle. *)
|
||||
WaitReady == /\ phase = "inspect" /\ selected # 0
|
||||
/\ objects[selected].life # "Available"
|
||||
/\ phase' = "pause"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
BeginClean == /\ phase = "inspect" /\ selected # 0
|
||||
/\ objects[selected].life = "Available"
|
||||
/\ ResourceKind = "CPE" \/ Mode = "Current" \/ routeSeen = VIP
|
||||
/\ todo' = IF selected \in health THEN Old ELSE {}
|
||||
/\ phase' = "clean"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, refs, dropped, restarted, badPublish>>
|
||||
|
||||
(* CPE disposal follows child-IPsec retirement; this abstracts that completed subprotocol. *)
|
||||
RetireChild(i) == /\ ResourceKind = "CPE" /\ phase = "clean" /\ i \in todo /\ i \in refs
|
||||
/\ refs' = refs \ {i}
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId,
|
||||
epoch, phase, seen, selected, attempt, reply, routeSeen,
|
||||
pending, health, published, admitted, todo, dropped,
|
||||
restarted, badPublish>>
|
||||
|
||||
Delete(i) == /\ phase = "clean" /\ i \in todo /\ i \notin refs
|
||||
/\ objects' = IF i \in Active
|
||||
THEN [objects EXCEPT ![i].life = "Terminating"]
|
||||
ELSE objects
|
||||
/\ todo' = todo \ {i}
|
||||
/\ health' = health \ {i}
|
||||
/\ UNCHANGED <<visible, tokens, invalid, issued, nextId, epoch, phase,
|
||||
seen, selected, attempt, reply, routeSeen, pending,
|
||||
published, admitted, refs, dropped, restarted, badPublish>>
|
||||
|
||||
CleanDone == /\ phase = "clean" /\ todo = {}
|
||||
/\ phase' = "admit"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
Admit == /\ phase = "admit"
|
||||
/\ admitted' = {i \in seen : objects[i].life = "Available"}
|
||||
/\ phase' = "publish"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
(* controller.Runner.step publishes after admission and two retrievable peer configurations. *)
|
||||
Publish == /\ phase = "publish"
|
||||
/\ published' = [epoch |-> epoch, id |-> selected, route |-> VIP]
|
||||
/\ badPublish' = (badPublish \/ (ResourceKind = "VPN" /\ routeSeen # VIP))
|
||||
/\ refs' = IF ResourceKind = "CPE" THEN refs \cup {selected} ELSE refs
|
||||
/\ phase' = "pause"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
admitted, todo, dropped, restarted>>
|
||||
|
||||
Poll == /\ phase = "pause" /\ phase' = "read"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, epoch,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
Provision(i) == /\ objects[i].life = "Provisioning"
|
||||
/\ objects' = [objects EXCEPT ![i].life = "Available"]
|
||||
/\ UNCHANGED <<visible, tokens, invalid, issued, nextId, epoch,
|
||||
phase, seen, selected, attempt, reply, routeSeen, pending,
|
||||
health, published, admitted, todo, refs, dropped,
|
||||
restarted, badPublish>>
|
||||
|
||||
Expose(i) == /\ i \in Present /\ i \notin visible
|
||||
/\ visible' = visible \cup {i}
|
||||
/\ UNCHANGED <<objects, tokens, invalid, issued, nextId, epoch, phase,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
Finish(i) == /\ objects[i].life = "Terminating"
|
||||
/\ objects' = [objects EXCEPT ![i].life =
|
||||
IF ResourceKind = "VPN" THEN "Terminated" ELSE "Absent"]
|
||||
/\ invalid' = invalid \cup {k \in Keys : tokens[k] = i}
|
||||
/\ UNCHANGED <<visible, tokens, issued, nextId, epoch, phase, seen,
|
||||
selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
Purge(i) == /\ objects[i].life = "Terminated"
|
||||
/\ objects' = [objects EXCEPT ![i].life = "Absent"]
|
||||
/\ UNCHANGED <<visible, tokens, invalid, issued, nextId, epoch, phase,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
Forget(i) == /\ objects[i].life = "Absent" /\ i \in visible
|
||||
/\ visible' = visible \ {i}
|
||||
/\ UNCHANGED <<objects, tokens, invalid, issued, nextId, epoch, phase,
|
||||
seen, selected, attempt, reply, routeSeen, pending, health,
|
||||
published, admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
ApplyRoute(i) == /\ pending[i] \in VIPs /\ objects[i].life = "Available"
|
||||
/\ objects' = [objects EXCEPT ![i].route = pending[i]]
|
||||
/\ pending' = [pending EXCEPT ![i] = "None"]
|
||||
/\ UNCHANGED <<visible, tokens, invalid, issued, nextId, epoch,
|
||||
phase, seen, selected, attempt, reply, routeSeen,
|
||||
health, published, admitted, todo, refs, dropped,
|
||||
restarted, badPublish>>
|
||||
|
||||
(* health denotes both associated IPsec tunnels being up; CPE itself has no health field. *)
|
||||
Establish(i) == /\ published.id = i /\ objects[i].life = "Available"
|
||||
/\ objects[i].wan = WAN /\ i \notin health
|
||||
/\ health' = health \cup {i}
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId,
|
||||
epoch, phase, seen, selected, attempt, reply, routeSeen,
|
||||
pending, published, admitted, todo, refs, dropped,
|
||||
restarted, badPublish>>
|
||||
|
||||
Converged == /\ published.epoch = epoch /\ published.id \in health
|
||||
/\ objects[published.id].wan = WAN
|
||||
/\ ResourceKind = "CPE" \/ objects[published.id].route = VIP
|
||||
|
||||
Advance == /\ epoch < LastEpoch /\ phase = "pause" /\ Converged
|
||||
/\ \A i \in Ids : i = published.id \/ objects[i].life \in {"Unused", "Absent"}
|
||||
/\ visible = {published.id}
|
||||
/\ epoch' = epoch + 1
|
||||
/\ health' = {}
|
||||
/\ phase' = "read"
|
||||
/\ UNCHANGED <<objects, visible, tokens, invalid, issued, nextId, seen,
|
||||
selected, attempt, reply, routeSeen, pending, published,
|
||||
admitted, todo, refs, dropped, restarted, badPublish>>
|
||||
|
||||
Work == Read \/ Select \/ Create \/ Replay \/ Reject \/ Receive \/ UpdateRoute \/
|
||||
WaitReady \/ BeginClean \/ CleanDone \/ Admit \/ Publish \/ Poll \/
|
||||
(\E i \in Ids : RetireChild(i) \/ Delete(i))
|
||||
|
||||
Provider(i) == Provision(i) \/ Expose(i) \/ Finish(i) \/ Purge(i) \/ Forget(i) \/
|
||||
ApplyRoute(i) \/ Establish(i)
|
||||
|
||||
Next == Work \/ Advance \/ DropReply \/ Restart \/ (\E i \in Ids : Provider(i))
|
||||
|
||||
(* Liveness assumes a stable final target and eventual API visibility, provisioning, and tunnel progress. *)
|
||||
Fairness == /\ WF_vars(Work)
|
||||
/\ SF_vars(Advance)
|
||||
/\ \A i \in Ids : /\ WF_vars(Provision(i)) /\ WF_vars(Expose(i))
|
||||
/\ WF_vars(Finish(i)) /\ WF_vars(Purge(i))
|
||||
/\ WF_vars(Forget(i)) /\ WF_vars(ApplyRoute(i))
|
||||
/\ WF_vars(Establish(i))
|
||||
|
||||
Spec == Init /\ [][Next]_vars /\ Fairness
|
||||
|
||||
TypeOK == /\ objects \in [Ids -> [life : Lives, wan : WANs, route : VIPs]]
|
||||
/\ visible \subseteq Ids /\ seen \subseteq Ids
|
||||
/\ tokens \in [Keys -> (Ids \cup {0})] /\ invalid \subseteq Keys
|
||||
/\ issued \subseteq (Keys \X Ids) /\ nextId \in 1..4
|
||||
/\ epoch \in 0..LastEpoch /\ phase \in Phases
|
||||
/\ selected \in Ids \cup {0} /\ reply \in Ids \cup {0}
|
||||
/\ attempt \in Attempts /\ routeSeen \in VIPs
|
||||
/\ pending \in [Ids -> VIPs \cup {"None"}]
|
||||
/\ health \subseteq Ids /\ admitted \subseteq Ids
|
||||
/\ todo \subseteq Ids /\ refs \subseteq Ids
|
||||
/\ published \in [epoch : 0..3, id : Ids \cup {0}, route : VIPs]
|
||||
/\ dropped \in BOOLEAN /\ restarted \in BOOLEAN /\ badPublish \in BOOLEAN
|
||||
|
||||
(* OCI Retry Token guarantees expire after 24 hours; expiry is outside this projection. *)
|
||||
(* https://docs.oracle.com/en-us/iaas/Content/API/Concepts/usingapi.htm *)
|
||||
OCIRetry == \A a, b \in issued : a[1] = b[1] => a[2] = b[2]
|
||||
(* DeleteCpe requires the CPE to be disconnected from its DRG. *)
|
||||
(* https://docs.oracle.com/en-us/iaas/tools/python/latest/api/core/client/oci.core.VirtualNetworkClient.html#oci.core.VirtualNetworkClient.delete_cpe *)
|
||||
OCIDelete == \A i \in refs : objects[i].life \notin {"Terminating", "Terminated", "Absent"}
|
||||
OCITokens == \A k \in invalid : tokens[k] # 0
|
||||
|
||||
RoutePublish == ~badPublish
|
||||
Admission == phase = "publish" => selected \in admitted
|
||||
Recovery == <>[](epoch = LastEpoch /\ Converged)
|
||||
=============================================================================
|
||||
146
docs/spec/tier-s/Lease.tla
Normal file
146
docs/spec/tier-s/Lease.tla
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
---- MODULE Lease ----
|
||||
EXTENDS Naturals
|
||||
|
||||
\* Equal clocks and urgent local steps project the bounded synthetic child fixture.
|
||||
\* The invariant counts live processes; it makes no claim about kernel SA cleanup.
|
||||
CONSTANTS LeaseSpan, ReplyMax
|
||||
ASSUME LeaseSpan \in Nat \ {0} /\ ReplyMax \in 0..20
|
||||
|
||||
Retry == 5
|
||||
Renew == 20
|
||||
StopWait == 10
|
||||
PollLow == 5
|
||||
PollHigh == 11
|
||||
Limit == LeaseSpan + 2 * PollHigh + ReplyMax + Retry + Renew + StopWait
|
||||
|
||||
VARIABLES now, version, seen, age, committed, leader, remaining,
|
||||
signalled, oldAlive, standby, pollLeft, newAlive
|
||||
vars == <<now, version, seen, age, committed, leader, remaining,
|
||||
signalled, oldAlive, standby, pollLeft, newAlive>>
|
||||
|
||||
TypeOK ==
|
||||
/\ now \in 0..Limit
|
||||
/\ version \in 0..2 /\ seen \in 0..2 /\ age \in 0..LeaseSpan
|
||||
/\ leader \in {"Pending", "Retry", "Renew", "Stopping", "Done"}
|
||||
/\ standby \in {"Poll", "Check", "Acquired", "Callback", "Running"}
|
||||
/\ remaining \in 0..Renew /\ pollLeft \in 0..PollHigh
|
||||
/\ \A value \in {committed, signalled, oldAlive, newAlive} : value \in BOOLEAN
|
||||
|
||||
\* The incumbent is running; one final renewal commits before later requests fail.
|
||||
Init ==
|
||||
/\ now = 0 /\ version = 0 /\ seen = 0 /\ age = 0
|
||||
/\ committed = FALSE /\ leader = "Pending" /\ remaining = 0
|
||||
/\ signalled = FALSE /\ oldAlive = TRUE
|
||||
/\ standby = "Poll" /\ pollLeft = 0 /\ newAlive = FALSE
|
||||
|
||||
Commit ==
|
||||
/\ ~committed
|
||||
/\ committed' = TRUE /\ version' = 1
|
||||
/\ UNCHANGED <<now, seen, age, leader, remaining, signalled, oldAlive,
|
||||
standby, pollLeft, newAlive>>
|
||||
|
||||
\* RenewDeadline bounds this successful response; the next retry starts afterward.
|
||||
Reply ==
|
||||
/\ committed /\ leader = "Pending" /\ now <= ReplyMax
|
||||
/\ leader' = "Retry" /\ remaining' = Retry
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, signalled, oldAlive,
|
||||
standby, pollLeft, newAlive>>
|
||||
|
||||
BeginRenew ==
|
||||
/\ leader = "Retry" /\ remaining = 0
|
||||
/\ leader' = "Renew" /\ remaining' = Renew
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, signalled, oldAlive,
|
||||
standby, pollLeft, newAlive>>
|
||||
|
||||
Cancel ==
|
||||
/\ leader = "Renew" /\ remaining = 0
|
||||
/\ leader' = "Stopping" /\ remaining' = StopWait
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, signalled, oldAlive,
|
||||
standby, pollLeft, newAlive>>
|
||||
|
||||
Signal ==
|
||||
/\ leader = "Stopping" /\ ~signalled
|
||||
/\ signalled' = TRUE
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, leader, remaining,
|
||||
oldAlive, standby, pollLeft, newAlive>>
|
||||
|
||||
Reap ==
|
||||
/\ leader = "Stopping" /\ signalled /\ oldAlive
|
||||
/\ oldAlive' = FALSE
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, leader, remaining,
|
||||
signalled, standby, pollLeft, newAlive>>
|
||||
|
||||
Return ==
|
||||
/\ leader = "Stopping" /\ ~oldAlive
|
||||
/\ leader' = "Done" /\ remaining' = 0
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, signalled, oldAlive,
|
||||
standby, pollLeft, newAlive>>
|
||||
|
||||
\* An unchanged Lease does not reset the contender's local observation age.
|
||||
Observe ==
|
||||
/\ standby = "Poll" /\ pollLeft = 0
|
||||
/\ age' = IF seen = version THEN age ELSE 0
|
||||
/\ seen' = version /\ standby' = "Check"
|
||||
/\ UNCHANGED <<now, version, committed, leader, remaining,
|
||||
signalled, oldAlive, pollLeft, newAlive>>
|
||||
|
||||
Defer ==
|
||||
/\ standby = "Check" /\ (age < LeaseSpan \/ seen /= version)
|
||||
/\ standby' = "Poll" /\ pollLeft' \in PollLow..PollHigh
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, leader, remaining,
|
||||
signalled, oldAlive, newAlive>>
|
||||
|
||||
Acquire ==
|
||||
/\ standby = "Check" /\ age = LeaseSpan /\ seen = version
|
||||
/\ version' = 2 /\ seen' = 2 /\ age' = 0
|
||||
/\ standby' = "Acquired"
|
||||
/\ UNCHANGED <<now, committed, leader, remaining,
|
||||
signalled, oldAlive, pollLeft, newAlive>>
|
||||
|
||||
Start ==
|
||||
/\ standby = "Acquired"
|
||||
/\ standby' = "Callback"
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, leader, remaining,
|
||||
signalled, oldAlive, pollLeft, newAlive>>
|
||||
|
||||
Spawn ==
|
||||
/\ standby = "Callback"
|
||||
/\ standby' = "Running" /\ newAlive' = TRUE
|
||||
/\ UNCHANGED <<now, version, seen, age, committed, leader, remaining,
|
||||
signalled, oldAlive, pollLeft>>
|
||||
|
||||
Done == leader = "Done" /\ standby = "Running"
|
||||
|
||||
\* Deadlines and local callbacks run before the next one-second clock step.
|
||||
Urgent ==
|
||||
\/ ~committed
|
||||
\/ (leader = "Pending" /\ now = ReplyMax)
|
||||
\/ (leader \in {"Retry", "Renew"} /\ remaining = 0)
|
||||
\/ (leader = "Stopping" /\ (~signalled \/ ~oldAlive \/ remaining = 0))
|
||||
\/ (standby = "Poll" /\ pollLeft = 0)
|
||||
\/ standby \in {"Check", "Acquired", "Callback"}
|
||||
|
||||
Tick ==
|
||||
/\ ~Done /\ ~Urgent
|
||||
/\ now' = now + 1
|
||||
/\ remaining' = IF leader \in {"Retry", "Renew", "Stopping"}
|
||||
THEN remaining - 1 ELSE remaining
|
||||
/\ age' = IF standby = "Poll" /\ age < LeaseSpan THEN age + 1 ELSE age
|
||||
/\ pollLeft' = IF standby = "Poll" THEN pollLeft - 1 ELSE pollLeft
|
||||
/\ UNCHANGED <<version, seen, committed, leader, signalled,
|
||||
oldAlive, standby, newAlive>>
|
||||
|
||||
Next == Commit \/ Reply \/ BeginRenew \/ Cancel \/ Signal \/ Reap \/ Return
|
||||
\/ Observe \/ Defer \/ Acquire \/ Start \/ Spawn \/ Tick
|
||||
\/ (Done /\ UNCHANGED vars)
|
||||
|
||||
Spec ==
|
||||
/\ Init /\ [][Next]_vars
|
||||
/\ WF_vars(Commit) /\ WF_vars(Reply) /\ WF_vars(BeginRenew)
|
||||
/\ WF_vars(Cancel) /\ WF_vars(Signal) /\ WF_vars(Reap) /\ WF_vars(Return)
|
||||
/\ WF_vars(Observe) /\ WF_vars(Defer) /\ WF_vars(Acquire)
|
||||
/\ WF_vars(Start) /\ WF_vars(Spawn) /\ WF_vars(Tick)
|
||||
|
||||
ProcessExclusion == ~(oldAlive /\ newAlive)
|
||||
Handoff == <>Done
|
||||
====
|
||||
141
docs/spec/tier-s/Runtime.tla
Normal file
141
docs/spec/tier-s/Runtime.tla
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
---- MODULE Runtime ----
|
||||
EXTENDS Naturals, FiniteSets
|
||||
|
||||
\* Incumbent-owner projection of e063bc9 run.go/parameters.yaml, after successful setup and claim.
|
||||
\* Probe/endpoint convergence is asynchronous; shutdown here is graceful, unlike Lease.tla.
|
||||
CONSTANTS Fixed, PublishUnready, Scenario
|
||||
ASSUME Fixed \in BOOLEAN /\ PublishUnready \in BOOLEAN /\ Scenario \in {"health", "ports", "proxy", "outage"}
|
||||
|
||||
Heads == {0, 1}
|
||||
None == 2
|
||||
VARIABLES stage, daemon, configured, label, sas, sample, route, flag,
|
||||
observedHealthy, reported, podReady, endpoint, proxy,
|
||||
ports, appliedPorts, portRestart, lost, reachable
|
||||
vars == <<stage, daemon, configured, label, sas, sample, route, flag,
|
||||
observedHealthy, reported, podReady, endpoint, proxy,
|
||||
ports, appliedPorts, portRestart, lost, reachable>>
|
||||
|
||||
TypeOK ==
|
||||
/\ stage \in {"Load", "Scan", "Route", "Report", "Idle", "Stop"}
|
||||
/\ sas \subseteq Heads /\ sample \subseteq Heads
|
||||
/\ route \in Heads \cup {None}
|
||||
/\ ports \in 0..1 /\ appliedPorts \in 0..1
|
||||
/\ \A value \in {daemon, configured, label, flag, observedHealthy,
|
||||
reported, podReady, endpoint, proxy, portRestart, lost, reachable} : value \in BOOLEAN
|
||||
|
||||
Init ==
|
||||
/\ stage = "Load" /\ daemon = FALSE /\ configured = FALSE /\ label = FALSE
|
||||
/\ sas = {} /\ sample = {} /\ route = None /\ flag = FALSE
|
||||
/\ observedHealthy = FALSE /\ reported = FALSE
|
||||
/\ podReady = FALSE /\ endpoint = FALSE /\ proxy = TRUE
|
||||
/\ ports = 0 /\ appliedPorts = 0 /\ portRestart = FALSE /\ lost = FALSE /\ reachable = TRUE
|
||||
|
||||
Load ==
|
||||
/\ stage = "Load"
|
||||
/\ daemon' = TRUE /\ configured' = TRUE /\ label' = TRUE
|
||||
/\ stage' = "Scan" /\ appliedPorts' = ports /\ flag' = FALSE
|
||||
/\ UNCHANGED <<sas, sample, route, observedHealthy, reported, podReady,
|
||||
endpoint, proxy, ports, portRestart, lost, reachable>>
|
||||
|
||||
Scan ==
|
||||
/\ stage = "Scan" /\ daemon
|
||||
/\ sample' = sas /\ stage' = "Route"
|
||||
/\ UNCHANGED <<daemon, configured, label, sas, route, flag, observedHealthy,
|
||||
reported, podReady, endpoint, proxy, ports, appliedPorts, portRestart, lost, reachable>>
|
||||
|
||||
Route ==
|
||||
/\ stage = "Route"
|
||||
/\ route' = IF sample = {} THEN route ELSE IF 0 \in sample THEN 0 ELSE 1
|
||||
/\ stage' = "Report"
|
||||
/\ UNCHANGED <<daemon, configured, label, sas, sample, flag, observedHealthy,
|
||||
reported, podReady, endpoint, proxy, ports, appliedPorts, portRestart, lost, reachable>>
|
||||
|
||||
Report ==
|
||||
/\ stage = "Report"
|
||||
/\ flag' = IF Fixed THEN sample /= {} ELSE TRUE
|
||||
/\ observedHealthy' = (sample /= {} /\ route \in sample)
|
||||
/\ reported' = TRUE /\ stage' = "Idle"
|
||||
/\ UNCHANGED <<daemon, configured, label, sas, sample, route, podReady,
|
||||
endpoint, proxy, ports, appliedPorts, portRestart, lost, reachable>>
|
||||
|
||||
Poll ==
|
||||
/\ stage = "Idle" /\ ports = appliedPorts
|
||||
/\ stage' = "Scan"
|
||||
/\ UNCHANGED <<daemon, configured, label, sas, sample, route, flag, observedHealthy,
|
||||
reported, podReady, endpoint, proxy, ports, appliedPorts, portRestart, lost, reachable>>
|
||||
|
||||
ChangePorts ==
|
||||
/\ Scenario = "ports" /\ ports = 0 /\ stage = "Idle" /\ daemon
|
||||
/\ sas /= {} /\ route \in sas
|
||||
/\ ports' = 1
|
||||
/\ UNCHANGED <<stage, daemon, configured, label, sas, sample, route, flag,
|
||||
observedHealthy, reported, podReady, endpoint, proxy, appliedPorts,
|
||||
portRestart, lost, reachable>>
|
||||
|
||||
Reload ==
|
||||
/\ stage = "Idle" /\ ports /= appliedPorts
|
||||
/\ stage' = IF Fixed THEN "Scan" ELSE "Stop"
|
||||
/\ appliedPorts' = IF Fixed THEN ports ELSE appliedPorts
|
||||
/\ UNCHANGED <<daemon, configured, label, sas, sample, route, flag, observedHealthy,
|
||||
reported, podReady, endpoint, proxy, ports, portRestart, lost, reachable>>
|
||||
|
||||
Stop ==
|
||||
/\ stage = "Stop"
|
||||
/\ daemon' = FALSE /\ sas' = {} /\ flag' = FALSE /\ stage' = "Load"
|
||||
/\ portRestart' = (portRestart \/ (daemon /\ ports /= appliedPorts))
|
||||
/\ UNCHANGED <<configured, label, sample, route, observedHealthy, reported,
|
||||
podReady, endpoint, proxy, ports, appliedPorts, lost, reachable>>
|
||||
|
||||
Probe ==
|
||||
LET desired == configured /\ flag /\ (Fixed \/ proxy)
|
||||
IN /\ podReady /= desired /\ podReady' = desired
|
||||
/\ UNCHANGED <<stage, daemon, configured, label, sas, sample, route, flag,
|
||||
observedHealthy, reported, endpoint, proxy, ports, appliedPorts,
|
||||
portRestart, lost, reachable>>
|
||||
|
||||
\* Publishing the active endpoint before serving readiness breaks the initial IKE dependency cycle.
|
||||
Realize ==
|
||||
LET desired == label /\ (PublishUnready \/ podReady)
|
||||
IN /\ endpoint /= desired /\ endpoint' = desired
|
||||
/\ UNCHANGED <<stage, daemon, configured, label, sas, sample, route, flag,
|
||||
observedHealthy, reported, podReady, proxy, ports, appliedPorts,
|
||||
portRestart, lost, reachable>>
|
||||
|
||||
Negotiate ==
|
||||
/\ daemon /\ configured /\ endpoint /\ reachable /\ sas /= Heads
|
||||
/\ sas' \in SUBSET Heads /\ sas \subseteq sas' /\ sas' /= sas
|
||||
/\ UNCHANGED <<stage, daemon, configured, label, sample, route, flag, observedHealthy,
|
||||
reported, podReady, endpoint, proxy, ports, appliedPorts, portRestart, lost, reachable>>
|
||||
|
||||
LosePeers ==
|
||||
/\ ~lost /\ sas = Heads
|
||||
/\ IF Scenario = "outage" THEN sas' = {} ELSE sas' \in (SUBSET Heads) \ {Heads}
|
||||
/\ lost' = TRUE
|
||||
/\ reachable' = (Scenario /= "outage")
|
||||
/\ UNCHANGED <<stage, daemon, configured, label, sample, route, flag, observedHealthy,
|
||||
reported, podReady, endpoint, proxy, ports, appliedPorts, portRestart>>
|
||||
|
||||
ProxyFails ==
|
||||
/\ Scenario = "proxy" /\ proxy /\ podReady /\ endpoint
|
||||
/\ sas /= {} /\ route \in sas
|
||||
/\ proxy' = FALSE
|
||||
/\ UNCHANGED <<stage, daemon, configured, label, sas, sample, route, flag,
|
||||
observedHealthy, reported, podReady, endpoint, ports, appliedPorts,
|
||||
portRestart, lost, reachable>>
|
||||
|
||||
Next == Load \/ Scan \/ Route \/ Report \/ Poll \/ ChangePorts \/ Reload \/ Stop
|
||||
\/ Probe \/ Realize \/ Negotiate \/ LosePeers \/ ProxyFails
|
||||
|
||||
Spec == Init /\ [][Next]_vars
|
||||
/\ WF_vars(Load) /\ WF_vars(Scan) /\ WF_vars(Route) /\ WF_vars(Report)
|
||||
/\ WF_vars(Poll) /\ WF_vars(Reload) /\ WF_vars(Stop)
|
||||
/\ WF_vars(Probe) /\ WF_vars(Realize) /\ WF_vars(Negotiate)
|
||||
/\ SF_vars(ChangePorts) /\ SF_vars(ProxyFails) /\ SF_vars(LosePeers)
|
||||
|
||||
HealthyObservation == reported => (~flag \/ observedHealthy)
|
||||
PortContinuity == ~portRestart
|
||||
TransportRecovery == <>[]endpoint
|
||||
Usable == daemon /\ endpoint /\ sas /= {} /\ route \in sas /\ ports = appliedPorts
|
||||
ServingRecovery == (<>[]reachable) => <>[]Usable
|
||||
LossReported == (<>[]~reachable) => <>[](~flag /\ ~podReady)
|
||||
====
|
||||
10
docs/spec/tier-s/cloud-async.cfg
Normal file
10
docs/spec/tier-s/cloud-async.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Fixed"
|
||||
Scenario = "Retry"
|
||||
ResourceKind = "VPN"
|
||||
LoseReply = TRUE
|
||||
RestartOnce = TRUE
|
||||
DelayLists = TRUE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission RoutePublish
|
||||
PROPERTY Recovery
|
||||
10
docs/spec/tier-s/cloud-cpe-async.cfg
Normal file
10
docs/spec/tier-s/cloud-cpe-async.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Fixed"
|
||||
Scenario = "Retry"
|
||||
ResourceKind = "CPE"
|
||||
LoseReply = TRUE
|
||||
RestartOnce = TRUE
|
||||
DelayLists = TRUE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission RoutePublish
|
||||
PROPERTY Recovery
|
||||
10
docs/spec/tier-s/cloud-current.cfg
Normal file
10
docs/spec/tier-s/cloud-current.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Current"
|
||||
Scenario = "Retry"
|
||||
ResourceKind = "CPE"
|
||||
LoseReply = FALSE
|
||||
RestartOnce = FALSE
|
||||
DelayLists = FALSE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission
|
||||
PROPERTY Recovery
|
||||
10
docs/spec/tier-s/cloud-drift.cfg
Normal file
10
docs/spec/tier-s/cloud-drift.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Fixed"
|
||||
Scenario = "Routes"
|
||||
ResourceKind = "VPN"
|
||||
LoseReply = TRUE
|
||||
RestartOnce = TRUE
|
||||
DelayLists = TRUE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission RoutePublish
|
||||
PROPERTY Recovery
|
||||
10
docs/spec/tier-s/cloud-fixed.cfg
Normal file
10
docs/spec/tier-s/cloud-fixed.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Fixed"
|
||||
Scenario = "Retry"
|
||||
ResourceKind = "CPE"
|
||||
LoseReply = FALSE
|
||||
RestartOnce = FALSE
|
||||
DelayLists = FALSE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission RoutePublish
|
||||
PROPERTY Recovery
|
||||
10
docs/spec/tier-s/cloud-route-old.cfg
Normal file
10
docs/spec/tier-s/cloud-route-old.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Current"
|
||||
Scenario = "Routes"
|
||||
ResourceKind = "VPN"
|
||||
LoseReply = FALSE
|
||||
RestartOnce = FALSE
|
||||
DelayLists = FALSE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission RoutePublish
|
||||
PROPERTY Recovery
|
||||
10
docs/spec/tier-s/cloud-route.cfg
Normal file
10
docs/spec/tier-s/cloud-route.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Fixed"
|
||||
Scenario = "Routes"
|
||||
ResourceKind = "VPN"
|
||||
LoseReply = FALSE
|
||||
RestartOnce = FALSE
|
||||
DelayLists = FALSE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission RoutePublish
|
||||
PROPERTY Recovery
|
||||
10
docs/spec/tier-s/cloud-vpn-old.cfg
Normal file
10
docs/spec/tier-s/cloud-vpn-old.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
CONSTANTS
|
||||
Mode = "Current"
|
||||
Scenario = "Retry"
|
||||
ResourceKind = "VPN"
|
||||
LoseReply = FALSE
|
||||
RestartOnce = FALSE
|
||||
DelayLists = FALSE
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK OCIRetry OCIDelete OCITokens Admission RoutePublish
|
||||
PROPERTY Recovery
|
||||
3
docs/spec/tier-s/current-health.cfg
Normal file
3
docs/spec/tier-s/current-health.cfg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
CONSTANTS Fixed = FALSE PublishUnready = FALSE Scenario = "health"
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK HealthyObservation
|
||||
3
docs/spec/tier-s/current-ports.cfg
Normal file
3
docs/spec/tier-s/current-ports.cfg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
CONSTANTS Fixed = FALSE PublishUnready = FALSE Scenario = "ports"
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK PortContinuity
|
||||
4
docs/spec/tier-s/current-proxy.cfg
Normal file
4
docs/spec/tier-s/current-proxy.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONSTANTS Fixed = FALSE PublishUnready = FALSE Scenario = "proxy"
|
||||
SPECIFICATION Spec
|
||||
INVARIANT TypeOK
|
||||
PROPERTY TransportRecovery
|
||||
4
docs/spec/tier-s/lease-current.cfg
Normal file
4
docs/spec/tier-s/lease-current.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONSTANTS LeaseSpan = 30 ReplyMax = 0
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK ProcessExclusion
|
||||
PROPERTY Handoff
|
||||
4
docs/spec/tier-s/lease-fixed.cfg
Normal file
4
docs/spec/tier-s/lease-fixed.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONSTANTS LeaseSpan = 60 ReplyMax = 20
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK ProcessExclusion
|
||||
PROPERTY Handoff
|
||||
4
docs/spec/tier-s/lease-reply.cfg
Normal file
4
docs/spec/tier-s/lease-reply.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONSTANTS LeaseSpan = 40 ReplyMax = 20
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK ProcessExclusion
|
||||
PROPERTY Handoff
|
||||
4
docs/spec/tier-s/runtime-gated.cfg
Normal file
4
docs/spec/tier-s/runtime-gated.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONSTANTS Fixed = TRUE PublishUnready = FALSE Scenario = "health"
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK HealthyObservation PortContinuity
|
||||
PROPERTY ServingRecovery
|
||||
4
docs/spec/tier-s/runtime-health.cfg
Normal file
4
docs/spec/tier-s/runtime-health.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONSTANTS Fixed = TRUE PublishUnready = TRUE Scenario = "health"
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK HealthyObservation PortContinuity
|
||||
PROPERTIES TransportRecovery ServingRecovery LossReported
|
||||
4
docs/spec/tier-s/runtime-outage.cfg
Normal file
4
docs/spec/tier-s/runtime-outage.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONSTANTS Fixed = TRUE PublishUnready = TRUE Scenario = "outage"
|
||||
SPECIFICATION Spec
|
||||
INVARIANTS TypeOK HealthyObservation PortContinuity
|
||||
PROPERTIES TransportRecovery ServingRecovery LossReported
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue