microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/apply-async-process-wait-functionality

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

283lines · modecode

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