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 all 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
47 changes: 46 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,48 @@ pub proof fn vrs_non_interference_property_equivalent_to_lifted_vrs_non_interfer
);
}

// TODO: Prove this lemma.
// More comments sketching an informal proof in the body.
#[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);

// We've proved the first property of filtered_pods.
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());

// We now must prove that the number of elements of `filtered_pods` is equal to the number
// of matching pods. This is true by the way we construct `filtered_pods`.
assert(filtered_pods.len() == matching_pod_entries(vrs, s.resources()).len());

// We now must prove that the elements of `filtered_pods` are precisely the matching pod
// entries. This is also true by construction.
assert(filtered_pods.map_values(|p: PodView| p.marshal()).to_set() == matching_pod_entries(vrs, s.resources()).values());
}

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


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 |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