1use myko_macros::myko_command;
2use myko::command::{CommandContext, CommandError, CommandHandler};
3use serde_json::Value;
4
5use crate::{
6 derive_resources, GetAllInventorys, GetPinsByIds, GetPlaybooksByIds, GetRunsByIds, OutputLine,
7 OutputLineId, OutputStream, Pin, PinId, Playbook, PlaybookId, Run, RunId, RunStatus,
8};
9
10#[myko_command]
14pub struct RunPlaybook {
15 pub playbook_id: PlaybookId,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub extra_vars: Option<Value>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub limit: Option<String>,
20}
21
22impl CommandHandler for RunPlaybook {
23 fn execute(self, ctx: CommandContext) -> Result<(), CommandError> {
24 ctx.exec_query_first(GetPlaybooksByIds {
26 ids: vec![self.playbook_id.clone()],
27 })?
28 .ok_or_else(|| CommandError {
29 tx: ctx.tx().to_string(),
30 command_id: ctx.command_id.to_string(),
31 message: format!("Playbook {} not found", self.playbook_id.0),
32 })?;
33
34 let resources = match ctx.exec_query(GetAllInventorys {})?.into_iter().next() {
39 Some(inv) => derive_resources(self.limit.as_deref(), &inv),
40 None => Vec::new(),
41 };
42
43 let run_id = RunId::from(uuid::Uuid::new_v4().to_string());
44 let now = chrono::Utc::now().to_rfc3339();
45 let run = Run {
46 playbook_id: self.playbook_id,
47 status: RunStatus::Queued,
48 started_at: now,
49 finished_at: None,
50 exit_code: None,
51 extra_vars: self.extra_vars,
52 limit: self.limit,
53 resources,
54 host_stats: Default::default(),
55 client_id: None, id: run_id,
57 };
58 ctx.emit_set(&run)?;
59 Ok(())
60 }
61}
62
63#[myko_command]
67pub struct CancelRun {
68 pub run_id: RunId,
69}
70
71impl CommandHandler for CancelRun {
72 fn execute(self, ctx: CommandContext) -> Result<(), CommandError> {
73 let current = ctx
74 .exec_query_first(GetRunsByIds {
75 ids: vec![self.run_id.clone()],
76 })?
77 .ok_or_else(|| CommandError {
78 tx: ctx.tx().to_string(),
79 command_id: ctx.command_id.to_string(),
80 message: format!("Run {} not found", self.run_id.0),
81 })?;
82
83 if !matches!(current.status, RunStatus::Queued | RunStatus::Running) {
87 return Ok(());
88 }
89
90 let mut updated = (*current).clone();
91 updated.status = RunStatus::Cancelled;
92 ctx.emit_set(&updated)?;
93 Ok(())
94 }
95}
96
97#[myko_command]
101pub struct PinPlaybook {
102 pub playbook_id: PlaybookId,
103}
104
105impl CommandHandler for PinPlaybook {
106 fn execute(self, ctx: CommandContext) -> Result<(), CommandError> {
107 ctx.exec_query_first(GetPlaybooksByIds {
108 ids: vec![self.playbook_id.clone()],
109 })?
110 .ok_or_else(|| CommandError {
111 tx: ctx.tx().to_string(),
112 command_id: ctx.command_id.to_string(),
113 message: format!("Playbook {} not found", self.playbook_id.0),
114 })?;
115
116 let pin_id = PinId::from(self.playbook_id.0.clone());
117 if let Some(existing) = ctx.exec_query_first(GetPinsByIds {
118 ids: vec![pin_id.clone()],
119 })? {
120 if existing.locked {
121 return Ok(());
122 }
123 }
124
125 let pin = Pin {
126 playbook_id: self.playbook_id,
127 locked: false,
128 id: pin_id,
129 };
130 ctx.emit_set(&pin)?;
131 Ok(())
132 }
133}
134
135#[myko_command]
141pub struct ReportRunOutput {
142 pub run_id: RunId,
143 pub stream: OutputStream,
144 pub line: String,
145 pub seq: u64,
146}
147
148impl CommandHandler for ReportRunOutput {
149 fn execute(self, ctx: CommandContext) -> Result<(), CommandError> {
150 let entity = OutputLine {
151 id: OutputLineId::from(format!("{}:{}", self.run_id.0, self.seq)),
152 run_id: self.run_id,
153 stream: self.stream,
154 line: self.line,
155 seq: self.seq,
156 };
157 ctx.emit_set(&entity)?;
158 Ok(())
159 }
160}
161
162#[myko_command]
168pub struct ReportRunFinished {
169 pub run_id: RunId,
170 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub exit_code: Option<i32>,
172}
173
174impl CommandHandler for ReportRunFinished {
175 fn execute(self, ctx: CommandContext) -> Result<(), CommandError> {
176 let current = ctx
177 .exec_query_first(GetRunsByIds {
178 ids: vec![self.run_id.clone()],
179 })?
180 .ok_or_else(|| CommandError {
181 tx: ctx.tx().to_string(),
182 command_id: ctx.command_id.to_string(),
183 message: format!("Run {} not found", self.run_id.0),
184 })?;
185
186 let mut updated = (*current).clone();
187 if updated.status != RunStatus::Cancelled {
189 updated.status = if self.exit_code == Some(0) {
190 RunStatus::Completed
191 } else {
192 RunStatus::Failed
193 };
194 }
195 updated.exit_code = self.exit_code;
196 updated.finished_at = Some(chrono::Utc::now().to_rfc3339());
197 ctx.emit_set(&updated)?;
198 Ok(())
199 }
200}
201
202#[myko_command]
207pub struct ReportPlaybooks {
208 pub playbooks: Vec<Playbook>,
209}
210
211impl CommandHandler for ReportPlaybooks {
212 fn execute(self, ctx: CommandContext) -> Result<(), CommandError> {
213 for pb in self.playbooks {
214 ctx.emit_set(&pb)?;
215 }
216 Ok(())
217 }
218}