Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prove VReplicaSet WF1 Lemmas for Create Path #571

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ pub proof fn lemma_from_after_send_list_pods_req_to_receive_list_pods_resp(
assert(resp_objs == selected_elements.to_seq());
assert(selected_elements.contains(o));
}
seq_pred_false_on_all_elements_implies_empty_filter(resp_objs, |o: DynamicObjectView| PodView::unmarshal(o).is_err());
seq_pred_false_on_all_elements_is_equivalent_to_empty_filter(resp_objs, |o: DynamicObjectView| PodView::unmarshal(o).is_err());

assert({
&&& s_prime.in_flight().contains(resp_msg)
Expand Down Expand Up @@ -535,7 +535,7 @@ pub proof fn lemma_from_after_send_list_pods_req_to_receive_list_pods_resp(
assert(resp_objs == selected_elements.to_seq());
assert(selected_elements.contains(o));
}
seq_pred_false_on_all_elements_implies_empty_filter(resp_objs, |o: DynamicObjectView| PodView::unmarshal(o).is_err());
seq_pred_false_on_all_elements_is_equivalent_to_empty_filter(resp_objs, |o: DynamicObjectView| PodView::unmarshal(o).is_err());

assert({
&&& s_prime.in_flight().contains(resp_msg)
Expand Down
50 changes: 49 additions & 1 deletion src/v2/controllers/vreplicaset_controller/proof/helper_lemmas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use crate::temporal_logic::{defs::*, rules::*};
use crate::vreplicaset_controller::{
model::{install::*, reconciler::*},
trusted::{liveness_theorem::*, spec_types::*, step::*},
proof::{predicate::*},
proof::{helper_invariants, predicate::*},
};
use crate::vstd_ext::seq_lib::*;
use vstd::prelude::*;

verus! {
Expand Down Expand Up @@ -68,4 +69,51 @@ pub proof fn vrs_non_interference_property_equivalent_to_lifted_vrs_non_interfer
);
}

// TODO: Prove this lemma.
// Annoying sequence reasoning.
#[verifier(external_body)]
pub proof fn lemma_filtered_pods_set_equals_matching_pods(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codyjrivera could you write more descriptive comments for this lemma?

Copy link
Collaborator Author

@codyjrivera codyjrivera Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes -- I can write better comments.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this one already proved or not? Coz I still see the external_body annotation.

Copy link
Collaborator Author

@codyjrivera codyjrivera Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not proven yet -- I had proved the first of the conjuncts but not the other two -- I've outlined roughly what we need to do in the body.

I can try to prove it today --- Fridays are generally terrible for me schedule-wise though.

Copy link
Collaborator

@marshtompsxd marshtompsxd Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK feel free to merge this one when you are ready. I've done my review

s: ClusterState, vrs: VReplicaSetView, cluster: Cluster,
controller_id: int, resp_msg: Message
)
requires
resp_msg_is_the_in_flight_list_resp_at_after_list_pods_step(vrs, controller_id, resp_msg)(s),
ensures
({
let objs = resp_msg.content.get_list_response().res.unwrap();
let pods_or_none = objects_to_pods(objs);
let pods = pods_or_none.unwrap();
let filtered_pods = filter_pods(pods, vrs);
&&& filtered_pods.no_duplicates()
&&& filtered_pods.len() == matching_pod_entries(vrs, s.resources()).len()
&&& filtered_pods.map_values(|p: PodView| p.marshal()).to_set() == matching_pod_entries(vrs, s.resources()).values()
}),
{
let pre = resp_msg_is_the_in_flight_list_resp_at_after_list_pods_step(vrs, controller_id, resp_msg)(s);
let objs = resp_msg.content.get_list_response().res.unwrap();
let pods_or_none = objects_to_pods(objs);
let pods = pods_or_none.unwrap();
let filtered_pods = filter_pods(pods, vrs);

assert(pods.no_duplicates());
let pred = |pod: PodView|
pod.metadata.owner_references_contains(vrs.controller_owner_ref())
&& vrs.spec.selector.matches(pod.metadata.labels.unwrap_or(Map::empty()))
&& pod.metadata.deletion_timestamp.is_None();
seq_filter_preserves_no_duplicates(pods, pred);
assert(filtered_pods.no_duplicates());

assert(filtered_pods.len() == matching_pod_entries(vrs, s.resources()).len());
assume(filtered_pods.map_values(|p: PodView| p.marshal()).to_set() == matching_pod_entries(vrs, s.resources()).values());

// let filtered_pods_set = filtered_pods.map_values(|p: PodView| p.marshal()).to_set();
// let matching_pods_set = matching_pod_entries(vrs, s.resources()).values();
// assert forall |o: DynamicObjectView| #[trigger] filtered_pods_set.contains(o) implies matching_pods_set.contains(o) by {
// //assume(false);
// }
// assert forall |o: DynamicObjectView| #[trigger] matching_pods_set.contains(o) implies filtered_pods_set.contains(o) by {
// //assume(false);
// }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,75 @@ pub proof fn lemma_api_request_outside_create_or_delete_loop_maintains_matching_
_ => {}
};
}

// TODO: Prove this.
#[verifier(external_body)]
pub proof fn lemma_api_request_not_made_by_vrs_maintains_matching_pods(
marshtompsxd marked this conversation as resolved.
Show resolved Hide resolved
s: ClusterState, s_prime: ClusterState, vrs: VReplicaSetView, cluster: Cluster, controller_id: int,
diff: int, msg: Message, req_msg: Option<Message>
)
requires
req_msg.is_Some() ==> msg != req_msg.get_Some_0(),
req_msg == s.ongoing_reconciles(controller_id)[vrs.object_ref()].pending_req_msg,
cluster.controller_models.contains_pair(controller_id, vrs_controller_model()),
cluster.next_step(s, s_prime, Step::APIServerStep(Some(msg))),
Cluster::each_object_in_etcd_is_weakly_well_formed()(s),
cluster.each_builtin_object_in_etcd_is_well_formed()(s),
cluster.each_object_in_etcd_is_well_formed::<VReplicaSetView>()(s),
cluster.every_in_flight_req_msg_from_controller_has_valid_controller_id()(s),
helper_invariants::every_create_request_is_well_formed(cluster, controller_id)(s),
helper_invariants::no_pending_update_or_update_status_request_on_pods()(s),
helper_invariants::no_pending_create_or_delete_request_not_from_controller_on_pods()(s),
helper_invariants::every_create_matching_pod_request_implies_at_after_create_pod_step(vrs, controller_id)(s),
helper_invariants::every_delete_matching_pod_request_implies_at_after_delete_pod_step(vrs, controller_id)(s),
// forall |diff: usize| !(#[trigger] at_vrs_step_with_vrs(vrs, controller_id, VReplicaSetReconcileStep::AfterCreatePod(diff))(s)),
// forall |diff: usize| !(#[trigger] at_vrs_step_with_vrs(vrs, controller_id, VReplicaSetReconcileStep::AfterDeletePod(diff))(s)),
forall |other_id| cluster.controller_models.remove(controller_id).contains_key(other_id)
==> #[trigger] vrs_not_interfered_by(other_id)(s)
ensures
matching_pod_entries(vrs, s.resources()) == matching_pod_entries(vrs, s_prime.resources()),
{
if msg.src.is_Controller() {
let id = msg.src.get_Controller_0();
assert(
(id != controller_id ==> cluster.controller_models.remove(controller_id).contains_key(id)));
// Invoke non-interference lemma by trigger.
assert(id != controller_id ==> vrs_not_interfered_by(id)(s));
}

// Dispatch through all the requests which may mutate the k-v store.
let mutates_key = if msg.content.is_create_request() {
let req = msg.content.get_create_request();
Some(ObjectRef{
kind: req.obj.kind,
name: if req.obj.metadata.name.is_Some() {
req.obj.metadata.name.unwrap()
} else {
generate_name(s.api_server)
},
namespace: req.namespace,
})
} else if msg.content.is_delete_request() {
let req = msg.content.get_delete_request();
Some(req.key)
} else if msg.content.is_update_request() {
let req = msg.content.get_update_request();
Some(req.key())
} else if msg.content.is_update_status_request() {
let req = msg.content.get_update_status_request();
Some(req.key())
} else {
None
};

match mutates_key {
Some(key) => {
assert_maps_equal!(s.resources().remove(key) == s_prime.resources().remove(key));
assert_maps_equal!(matching_pod_entries(vrs, s.resources()) == matching_pod_entries(vrs, s_prime.resources()));
},
_ => {}
};
}


}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::kubernetes_api_objects::spec::prelude::*;
use crate::kubernetes_cluster::spec::{cluster::*, message::*};
use crate::temporal_logic::{defs::*, rules::*};
use crate::temporal_logic::{defs::*};
use crate::vreplicaset_controller::{
model::{install::*, reconciler::*},
trusted::{liveness_theorem::*, spec_types::*, step::*},
model::{install::*},
trusted::{liveness_theorem::*, spec_types::*},
};
use vstd::prelude::*;

Expand Down
Loading
Loading