microsoft/openvmm

Public

mirrored from https://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
b2022a9e898bbe95b50f3f79a32a3e254fef338b

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

flowey/flowey_cli/src/cli/debug/interrogate.rs

250lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::cli::FlowBackendCli;
5use flowey_core::node::steps::rust::RustRuntimeServices;
6use flowey_core::node::user_facing::ClaimedGhParam;
7use flowey_core::node::user_facing::GhPermission;
8use flowey_core::node::user_facing::GhPermissionValue;
9use flowey_core::node::FlowArch;
10use flowey_core::node::FlowBackend;
11use flowey_core::node::FlowPlatform;
12use flowey_core::node::GhVarState;
13use flowey_core::node::NodeHandle;
14use flowey_core::pipeline::HostExt;
15use flowey_core::pipeline::PipelineBackendHint;
16use std::collections::BTreeMap;
17
18/// (debug) get info about a specific node.
19///
20/// Information includes:
21/// - supported backends
22/// - supported requests
23/// - dependencies
24/// - inline steps (with exec-snippet indices)*
25///
26/// *inline-steps will only be listed if they are enabled via the particular
27/// combination of specified {backend x requests}. For a complete picture
28/// possible dependencies and available steps, you must inspect the Node's
29/// code and documentation directly.
30#[derive(clap::Args)]
31pub struct Interrogate {
32 /// Node to interrogate
33 node_handle: String,
34
35 /// Flow backend to interrogate with
36 flow_backend: FlowBackendCli,
37
38 /// Apply a request onto the node (as JSON)
39 #[clap(long)]
40 req: Vec<String>,
41}
42
43impl Interrogate {
44 pub fn run(self) -> anyhow::Result<()> {
45 let Self {
46 node_handle,
47 flow_backend,
48 req,
49 } = self;
50
51 let raw_json_reqs: Vec<Box<[u8]>> = req
52 .into_iter()
53 .map(|v| v.as_bytes().to_vec().into())
54 .collect();
55
56 let Some(node_handle) = NodeHandle::try_from_modpath(&node_handle) else {
57 anyhow::bail!("could not find node with that name");
58 };
59
60 let mut node = node_handle.new_erased_node();
61
62 let mut dep_registration_backend = InterrogateDepRegistrationBackend;
63 let mut dep_registration = flowey_core::node::new_import_ctx(&mut dep_registration_backend);
64
65 let mut ctx_backend = InterrogateCtx::new(flow_backend.into(), node_handle);
66
67 println!(
68 "# interrogating with {}",
69 match flow_backend {
70 FlowBackendCli::Ado => "ado",
71 FlowBackendCli::Local => "local",
72 FlowBackendCli::Github => "github",
73 }
74 );
75
76 node.imports(&mut dep_registration);
77
78 let mut ctx = flowey_core::node::new_node_ctx(&mut ctx_backend);
79 node.emit(raw_json_reqs.clone(), &mut ctx)?;
80
81 Ok(())
82 }
83}
84
85struct InterrogateDepRegistrationBackend;
86
87impl flowey_core::node::ImportCtxBackend for InterrogateDepRegistrationBackend {
88 fn on_possible_dep(&mut self, node_handle: NodeHandle) {
89 println!("[dep?] {}", node_handle.modpath())
90 }
91}
92
93struct InterrogateCtx {
94 flow_backend: FlowBackend,
95 current_node: NodeHandle,
96 idx_tracker: usize,
97 var_tracker: usize,
98}
99
100impl InterrogateCtx {
101 fn new(flow_backend: FlowBackend, current_node: NodeHandle) -> Self {
102 Self {
103 flow_backend,
104 current_node,
105 idx_tracker: 0,
106 var_tracker: 0,
107 }
108 }
109}
110
111impl flowey_core::node::NodeCtxBackend for InterrogateCtx {
112 fn on_emit_rust_step(
113 &mut self,
114 label: &str,
115 _code: Box<
116 dyn for<'a> FnOnce(&'a mut RustRuntimeServices<'_>) -> anyhow::Result<()> + 'static,
117 >,
118 ) {
119 println!("[step][rust][{}] # {}", self.idx_tracker, label);
120 self.idx_tracker += 1;
121 }
122
123 fn on_emit_ado_step(
124 &mut self,
125 label: &str,
126 yaml_snippet: Box<
127 dyn for<'a> FnOnce(
128 &'a mut flowey_core::node::user_facing::AdoStepServices<'_>,
129 ) -> String,
130 >,
131 code: Option<
132 Box<
133 dyn for<'a> FnOnce(&'a mut RustRuntimeServices<'_>) -> anyhow::Result<()> + 'static,
134 >,
135 >,
136 _condvar: Option<String>,
137 ) {
138 println!(
139 "[step][yaml] # {}{}",
140 if code.is_some() {
141 "(+inline script) "
142 } else {
143 ""
144 },
145 label
146 );
147 let mut fresh_ado_var = || "<dummy>".into();
148 let mut access = flowey_core::node::steps::ado::new_ado_step_services(&mut fresh_ado_var);
149 let raw_snippet = yaml_snippet(&mut access);
150
151 let snippet: Result<serde_yaml::Value, _> = serde_yaml::from_str(&raw_snippet);
152 match snippet {
153 Ok(snippet) => print!("{}", serde_yaml::to_string(&snippet).unwrap()),
154 Err(e) => {
155 log::error!("invalid snippet: {}", e);
156 println!(">>>");
157 println!("{}", raw_snippet);
158 println!("<<<");
159 }
160 };
161
162 self.idx_tracker += 1;
163 }
164
165 fn on_emit_gh_step(
166 &mut self,
167
168 label: &str,
169 _uses: &str,
170 _with: BTreeMap<String, ClaimedGhParam>,
171 _condvar: Option<String>,
172 _outputs: BTreeMap<String, Vec<GhVarState>>,
173 _permissions: BTreeMap<GhPermission, GhPermissionValue>,
174 _gh_to_rust: Vec<GhVarState>,
175 _rust_to_gh: Vec<GhVarState>,
176 ) {
177 println!("[step][yaml] # {}", label);
178 self.idx_tracker += 1;
179 }
180
181 fn on_emit_side_effect_step(&mut self) {
182 println!("[step][anchor]");
183 }
184
185 fn backend(&mut self) -> FlowBackend {
186 self.flow_backend
187 }
188
189 fn platform(&mut self) -> FlowPlatform {
190 FlowPlatform::host(PipelineBackendHint::Local)
191 }
192
193 fn arch(&mut self) -> FlowArch {
194 // xtask-fmt allow-target-arch oneoff-flowey
195 if cfg!(target_arch = "x86_64") {
196 FlowArch::X86_64
197 // xtask-fmt allow-target-arch oneoff-flowey
198 } else if cfg!(target_arch = "aarch64") {
199 FlowArch::Aarch64
200 } else {
201 unreachable!("flowey only runs on X86_64 or Aarch64 at the moment")
202 }
203 }
204
205 fn on_request(&mut self, node_handle: NodeHandle, req: anyhow::Result<Box<[u8]>>) {
206 match req {
207 Ok(data) => {
208 let data = match String::from_utf8(data.into()) {
209 Ok(data) => data,
210 Err(e) => e
211 .into_bytes()
212 .iter()
213 .map(|b| format!("(raw) {:02x}", b))
214 .collect::<Vec<_>>()
215 .join(""),
216 };
217 println!("[req] {} <-- {}", node_handle.modpath(), data)
218 }
219 Err(e) => {
220 log::error!("error serializing inter-node request: {:#}", e)
221 }
222 }
223 }
224
225 fn on_new_var(&mut self) -> String {
226 let v = self.var_tracker;
227 self.var_tracker += 1;
228 format!("<dummy>:{}", v)
229 }
230
231 fn on_claimed_runtime_var(&mut self, var: &str, is_read: bool) {
232 println!(
233 "[var][claim] {} {}",
234 var,
235 if is_read { "(read)" } else { "(write)" },
236 )
237 }
238
239 fn current_node(&self) -> NodeHandle {
240 self.current_node
241 }
242
243 fn persistent_dir_path_var(&mut self) -> Option<String> {
244 Some("<dummy>".into())
245 }
246
247 fn on_unused_read_var(&mut self, _var: &str) {
248 // not relevant
249 }
250}