# This test verifies that configuration changes are not permitted to compromise
# the safety of leader fortification.
#
# For details, see FortificationTracker.ConfigChangeSafe.
add-nodes 4 voters=(1, 2, 3) index=2
----
INFO 1 switched to configuration voters=(1 2 3)
INFO 1 became follower at term 0
DEBUG 1 reset election elapsed to 0
INFO newRaft 1 [peers: [1,2,3], term: 0, commit: 2, applied: 2, lastindex: 2, lastterm: 1]
INFO 2 switched to configuration voters=(1 2 3)
INFO 2 became follower at term 0
DEBUG 2 reset election elapsed to 0
INFO newRaft 2 [peers: [1,2,3], term: 0, commit: 2, applied: 2, lastindex: 2, lastterm: 1]
INFO 3 switched to configuration voters=(1 2 3)
INFO 3 became follower at term 0
DEBUG 3 reset election elapsed to 0
INFO newRaft 3 [peers: [1,2,3], term: 0, commit: 2, applied: 2, lastindex: 2, lastterm: 1]
INFO 4 switched to configuration voters=(1 2 3)
INFO 4 became follower at term 0
DEBUG 4 reset election elapsed to 0
INFO newRaft 4 [peers: [1,2,3], term: 0, commit: 2, applied: 2, lastindex: 2, lastterm: 1]

campaign 1
----
INFO 1 is starting a new election at term 0
INFO 1 became candidate at term 1
INFO 1 [logterm: 1, index: 2] sent MsgVote request to 2 at term 1
INFO 1 [logterm: 1, index: 2] sent MsgVote request to 3 at term 1

stabilize log-level=none
----
ok

# Withdraw store liveness support from 3 for 1 so that the lead support is
# fragile.
withdraw-support 3 1
----
  1 2 3 4
1 1 1 1 1
2 1 1 1 1
3 x 1 1 1
4 1 1 1 1

# Add v4.
propose-conf-change 1
v4
----
ok

stabilize 1 2 3
----
> 1 handling Ready
  Ready MustSync=true:
  Entries:
  1/4 EntryConfChangeV2 v4
  Messages:
  1->2 MsgApp Term:1 Log:1/3 Commit:3 Entries:[1/4 EntryConfChangeV2 v4]
  1->3 MsgApp Term:1 Log:1/3 Commit:3 Entries:[1/4 EntryConfChangeV2 v4]
> 2 receiving messages
  1->2 MsgApp Term:1 Log:1/3 Commit:3 Entries:[1/4 EntryConfChangeV2 v4]
> 3 receiving messages
  1->3 MsgApp Term:1 Log:1/3 Commit:3 Entries:[1/4 EntryConfChangeV2 v4]
> 2 handling Ready
  Ready MustSync=true:
  Entries:
  1/4 EntryConfChangeV2 v4
  Messages:
  2->1 MsgAppResp Term:1 Log:0/4 Commit:3
> 3 handling Ready
  Ready MustSync=true:
  Entries:
  1/4 EntryConfChangeV2 v4
  Messages:
  3->1 MsgAppResp Term:1 Log:0/4 Commit:3
> 1 receiving messages
  2->1 MsgAppResp Term:1 Log:0/4 Commit:3
  3->1 MsgAppResp Term:1 Log:0/4 Commit:3
> 1 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:4 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/4 EntryConfChangeV2 v4
  Messages:
  1->2 MsgApp Term:1 Log:1/4 Commit:4
  1->3 MsgApp Term:1 Log:1/4 Commit:4
  INFO 1 switched to configuration voters=(1 2 3 4)
> 2 receiving messages
  1->2 MsgApp Term:1 Log:1/4 Commit:4
> 3 receiving messages
  1->3 MsgApp Term:1 Log:1/4 Commit:4
> 1 handling Ready
  Ready MustSync=false:
  Messages:
  1->4 MsgFortifyLeader Term:1 Log:0/0
  1->4 MsgApp Term:1 Log:1/3 Commit:4 Entries:[1/4 EntryConfChangeV2 v4]
> 2 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:4 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/4 EntryConfChangeV2 v4
  Messages:
  2->1 MsgAppResp Term:1 Log:0/4 Commit:4
  INFO 2 switched to configuration voters=(1 2 3 4)
> 3 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:4 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/4 EntryConfChangeV2 v4
  Messages:
  3->1 MsgAppResp Term:1 Log:0/4 Commit:4
  INFO 3 switched to configuration voters=(1 2 3 4)
> 1 receiving messages
  2->1 MsgAppResp Term:1 Log:0/4 Commit:4
  3->1 MsgAppResp Term:1 Log:0/4 Commit:4

store-liveness
----
  1 2 3 4
1 1 1 1 1
2 1 1 1 1
3 x 1 1 1
4 1 1 1 1

print-fortification-state 1
----
1 : 1
2 : 1
3 : 1

# Try to demote v2. This should be rejected because v1's lead support under the
# current config has not caught up to the previous config.
propose-conf-change 1
r2 l2
----
INFO 1 ignoring conf change {ConfChangeTransitionAuto [{ConfChangeRemoveNode 2} {ConfChangeAddLearnerNode 2}] []} at config voters=(1 2 3 4): lead support has not caught up to previous configuration

# Fortify v4 so that lead support catches up.
stabilize 1 4
----
> 1 handling Ready
  Ready MustSync=true:
  Entries:
  1/5 EntryNormal ""
  Messages:
  1->2 MsgApp Term:1 Log:1/4 Commit:4 Entries:[1/5 EntryNormal ""]
  1->3 MsgApp Term:1 Log:1/4 Commit:4 Entries:[1/5 EntryNormal ""]
> 4 receiving messages
  1->4 MsgFortifyLeader Term:1 Log:0/0
  INFO 4 [term: 0] received a MsgFortifyLeader message with higher term from 1 [term: 1], new leader indicated, advancing term
  INFO 4 became follower at term 1
  DEBUG 4 reset election elapsed to 0
  1->4 MsgApp Term:1 Log:1/3 Commit:4 Entries:[1/4 EntryConfChangeV2 v4]
  DEBUG 4 [logterm: 0, index: 3] rejected MsgApp [logterm: 1, index: 3] from 1
> 4 handling Ready
  Ready MustSync=true:
  HardState Term:1 Commit:2 Lead:1 LeadEpoch:1
  Messages:
  4->1 MsgFortifyLeaderResp Term:1 Log:0/0 LeadEpoch:1
  4->1 MsgAppResp Term:1 Log:1/3 Rejected (Hint: 2) Commit:2
> 1 receiving messages
  4->1 MsgFortifyLeaderResp Term:1 Log:0/0 LeadEpoch:1
  4->1 MsgAppResp Term:1 Log:1/3 Rejected (Hint: 2) Commit:2
  DEBUG 1 received MsgAppResp(rejected, hint: (index 2, term 1)) from 4 for index 3
  DEBUG 1 decreased progress of 4 to [StateProbe match=0 next=3 sentCommit=2 matchCommit=2]
> 1 handling Ready
  Ready MustSync=false:
  Messages:
  1->4 MsgApp Term:1 Log:1/3 Commit:4 Entries:[
    1/4 EntryConfChangeV2 v4
    1/5 EntryNormal ""
  ]
  1->4 MsgApp Term:1 Log:1/2 Commit:4 Entries:[
    1/3 EntryNormal ""
    1/4 EntryConfChangeV2 v4
    1/5 EntryNormal ""
  ]
> 4 receiving messages
  1->4 MsgApp Term:1 Log:1/3 Commit:4 Entries:[
    1/4 EntryConfChangeV2 v4
    1/5 EntryNormal ""
  ]
  DEBUG 4 [logterm: 0, index: 3] rejected MsgApp [logterm: 1, index: 3] from 1
  1->4 MsgApp Term:1 Log:1/2 Commit:4 Entries:[
    1/3 EntryNormal ""
    1/4 EntryConfChangeV2 v4
    1/5 EntryNormal ""
  ]
> 4 handling Ready
  Ready MustSync=true:
  HardState Term:1 Commit:4 Lead:1 LeadEpoch:1
  Entries:
  1/3 EntryNormal ""
  1/4 EntryConfChangeV2 v4
  1/5 EntryNormal ""
  CommittedEntries:
  1/3 EntryNormal ""
  1/4 EntryConfChangeV2 v4
  Messages:
  4->1 MsgAppResp Term:1 Log:1/3 Rejected (Hint: 2) Commit:2
  4->1 MsgAppResp Term:1 Log:0/5 Commit:4
  INFO 4 switched to configuration voters=(1 2 3 4)
> 1 receiving messages
  4->1 MsgAppResp Term:1 Log:1/3 Rejected (Hint: 2) Commit:2
  DEBUG 1 received MsgAppResp(rejected, hint: (index 2, term 1)) from 4 for index 3
  4->1 MsgAppResp Term:1 Log:0/5 Commit:4

store-liveness
----
  1 2 3 4
1 1 1 1 1
2 1 1 1 1
3 x 1 1 1
4 1 1 1 1

print-fortification-state 1
----
1 : 1
2 : 1
3 : 1
4 : 1

# Try to demote v2 again. This time, the proposal should be allowed.
propose-conf-change 1
r2 l2
----
ok

stabilize
----
> 1 handling Ready
  Ready MustSync=true:
  Entries:
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  1->2 MsgApp Term:1 Log:1/5 Commit:4 Entries:[1/6 EntryConfChangeV2 r2 l2]
  1->3 MsgApp Term:1 Log:1/5 Commit:4 Entries:[1/6 EntryConfChangeV2 r2 l2]
  1->4 MsgApp Term:1 Log:1/5 Commit:4 Entries:[1/6 EntryConfChangeV2 r2 l2]
> 2 receiving messages
  1->2 MsgApp Term:1 Log:1/4 Commit:4 Entries:[1/5 EntryNormal ""]
  1->2 MsgApp Term:1 Log:1/5 Commit:4 Entries:[1/6 EntryConfChangeV2 r2 l2]
> 3 receiving messages
  1->3 MsgApp Term:1 Log:1/4 Commit:4 Entries:[1/5 EntryNormal ""]
  1->3 MsgApp Term:1 Log:1/5 Commit:4 Entries:[1/6 EntryConfChangeV2 r2 l2]
> 4 receiving messages
  1->4 MsgApp Term:1 Log:1/5 Commit:4 Entries:[1/6 EntryConfChangeV2 r2 l2]
> 2 handling Ready
  Ready MustSync=true:
  Entries:
  1/5 EntryNormal ""
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  2->1 MsgAppResp Term:1 Log:0/5 Commit:4
  2->1 MsgAppResp Term:1 Log:0/6 Commit:4
> 3 handling Ready
  Ready MustSync=true:
  Entries:
  1/5 EntryNormal ""
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  3->1 MsgAppResp Term:1 Log:0/5 Commit:4
  3->1 MsgAppResp Term:1 Log:0/6 Commit:4
> 4 handling Ready
  Ready MustSync=true:
  Entries:
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  4->1 MsgAppResp Term:1 Log:0/6 Commit:4
> 1 receiving messages
  2->1 MsgAppResp Term:1 Log:0/5 Commit:4
  2->1 MsgAppResp Term:1 Log:0/6 Commit:4
  3->1 MsgAppResp Term:1 Log:0/5 Commit:4
  3->1 MsgAppResp Term:1 Log:0/6 Commit:4
  4->1 MsgAppResp Term:1 Log:0/6 Commit:4
> 1 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:6 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/5 EntryNormal ""
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  1->2 MsgApp Term:1 Log:1/6 Commit:5
  1->3 MsgApp Term:1 Log:1/6 Commit:5
  1->4 MsgApp Term:1 Log:1/6 Commit:5
  1->2 MsgApp Term:1 Log:1/6 Commit:6
  1->3 MsgApp Term:1 Log:1/6 Commit:6
  1->4 MsgApp Term:1 Log:1/6 Commit:6
  INFO 1 switched to configuration voters=(1 3 4)&&(1 2 3 4) learners_next=(2) autoleave
  INFO initiating automatic transition out of joint configuration voters=(1 3 4)&&(1 2 3 4) learners_next=(2) autoleave
> 2 receiving messages
  1->2 MsgApp Term:1 Log:1/6 Commit:5
  1->2 MsgApp Term:1 Log:1/6 Commit:6
> 3 receiving messages
  1->3 MsgApp Term:1 Log:1/6 Commit:5
  1->3 MsgApp Term:1 Log:1/6 Commit:6
> 4 receiving messages
  1->4 MsgApp Term:1 Log:1/6 Commit:5
  1->4 MsgApp Term:1 Log:1/6 Commit:6
> 1 handling Ready
  Ready MustSync=true:
  Entries:
  1/7 EntryConfChangeV2
  Messages:
  1->2 MsgApp Term:1 Log:1/6 Commit:6 Entries:[1/7 EntryConfChangeV2]
  1->3 MsgApp Term:1 Log:1/6 Commit:6 Entries:[1/7 EntryConfChangeV2]
  1->4 MsgApp Term:1 Log:1/6 Commit:6 Entries:[1/7 EntryConfChangeV2]
> 2 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:6 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/5 EntryNormal ""
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  2->1 MsgAppResp Term:1 Log:0/6 Commit:5
  2->1 MsgAppResp Term:1 Log:0/6 Commit:6
  INFO 2 switched to configuration voters=(1 3 4)&&(1 2 3 4) learners_next=(2) autoleave
> 3 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:6 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/5 EntryNormal ""
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  3->1 MsgAppResp Term:1 Log:0/6 Commit:5
  3->1 MsgAppResp Term:1 Log:0/6 Commit:6
  INFO 3 switched to configuration voters=(1 3 4)&&(1 2 3 4) learners_next=(2) autoleave
> 4 handling Ready
  Ready MustSync=true:
  HardState Term:1 Commit:6 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/5 EntryNormal ""
  1/6 EntryConfChangeV2 r2 l2
  Messages:
  4->1 MsgAppResp Term:1 Log:0/6 Commit:5
  4->1 MsgAppResp Term:1 Log:0/6 Commit:6
  INFO 4 switched to configuration voters=(1 3 4)&&(1 2 3 4) learners_next=(2) autoleave
> 1 receiving messages
  2->1 MsgAppResp Term:1 Log:0/6 Commit:5
  2->1 MsgAppResp Term:1 Log:0/6 Commit:6
  3->1 MsgAppResp Term:1 Log:0/6 Commit:5
  3->1 MsgAppResp Term:1 Log:0/6 Commit:6
  4->1 MsgAppResp Term:1 Log:0/6 Commit:5
  4->1 MsgAppResp Term:1 Log:0/6 Commit:6
> 2 receiving messages
  1->2 MsgApp Term:1 Log:1/6 Commit:6 Entries:[1/7 EntryConfChangeV2]
> 3 receiving messages
  1->3 MsgApp Term:1 Log:1/6 Commit:6 Entries:[1/7 EntryConfChangeV2]
> 4 receiving messages
  1->4 MsgApp Term:1 Log:1/6 Commit:6 Entries:[1/7 EntryConfChangeV2]
> 2 handling Ready
  Ready MustSync=true:
  Entries:
  1/7 EntryConfChangeV2
  Messages:
  2->1 MsgAppResp Term:1 Log:0/7 Commit:6
> 3 handling Ready
  Ready MustSync=true:
  Entries:
  1/7 EntryConfChangeV2
  Messages:
  3->1 MsgAppResp Term:1 Log:0/7 Commit:6
> 4 handling Ready
  Ready MustSync=true:
  Entries:
  1/7 EntryConfChangeV2
  Messages:
  4->1 MsgAppResp Term:1 Log:0/7 Commit:6
> 1 receiving messages
  2->1 MsgAppResp Term:1 Log:0/7 Commit:6
  3->1 MsgAppResp Term:1 Log:0/7 Commit:6
  4->1 MsgAppResp Term:1 Log:0/7 Commit:6
> 1 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:7 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/7 EntryConfChangeV2
  Messages:
  1->2 MsgApp Term:1 Log:1/7 Commit:7
  1->3 MsgApp Term:1 Log:1/7 Commit:7
  1->4 MsgApp Term:1 Log:1/7 Commit:7
  INFO 1 switched to configuration voters=(1 3 4) learners=(2)
> 2 receiving messages
  1->2 MsgApp Term:1 Log:1/7 Commit:7
> 3 receiving messages
  1->3 MsgApp Term:1 Log:1/7 Commit:7
> 4 receiving messages
  1->4 MsgApp Term:1 Log:1/7 Commit:7
> 2 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:7 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/7 EntryConfChangeV2
  Messages:
  2->1 MsgAppResp Term:1 Log:0/7 Commit:7
  INFO 2 switched to configuration voters=(1 3 4) learners=(2)
> 3 handling Ready
  Ready MustSync=true:
  HardState Term:1 Vote:1 Commit:7 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/7 EntryConfChangeV2
  Messages:
  3->1 MsgAppResp Term:1 Log:0/7 Commit:7
  INFO 3 switched to configuration voters=(1 3 4) learners=(2)
> 4 handling Ready
  Ready MustSync=true:
  HardState Term:1 Commit:7 Lead:1 LeadEpoch:1
  CommittedEntries:
  1/7 EntryConfChangeV2
  Messages:
  4->1 MsgAppResp Term:1 Log:0/7 Commit:7
  INFO 4 switched to configuration voters=(1 3 4) learners=(2)
> 1 receiving messages
  2->1 MsgAppResp Term:1 Log:0/7 Commit:7
  3->1 MsgAppResp Term:1 Log:0/7 Commit:7
  4->1 MsgAppResp Term:1 Log:0/7 Commit:7
