1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
use async_timing_util::unix_timestamp_ms;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
use typeshare::typeshare;
use crate::entities::{
I64, MongoId, Operation, all_logs_success, komodo_timestamp,
};
use super::{ResourceTarget, Version};
/// Represents an action performed by Komodo.
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(
feature = "mongo",
derive(mongo_indexed::derive::MongoIndexed)
)]
#[cfg_attr(feature = "mongo", doc_index({ "target.type": 1 }))]
#[cfg_attr(feature = "mongo", sparse_doc_index({ "target.id": 1 }))]
pub struct Update {
/// The Mongo ID of the update.
/// This field is de/serialized from/to JSON as
/// `{ "_id": { "$oid": "..." }, ...(rest of serialized Update) }`
#[serde(
default,
rename = "_id",
skip_serializing_if = "String::is_empty",
with = "bson::serde_helpers::hex_string_as_object_id"
)]
pub id: MongoId,
/// The operation performed
#[cfg_attr(feature = "mongo", index)]
pub operation: Operation,
/// The time the operation started
#[cfg_attr(feature = "mongo", index)]
pub start_ts: I64,
/// Whether the operation was successful
#[cfg_attr(feature = "mongo", index)]
pub success: bool,
/// The user id that triggered the update.
///
/// Also can take these values for operations triggered automatically:
/// - `Procedure`: The operation was triggered as part of a procedure run
/// - `Github`: The operation was triggered by a github webhook
/// - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing.
#[cfg_attr(feature = "mongo", index)]
pub operator: String,
/// The target resource to which this update refers
pub target: ResourceTarget,
/// Logs produced as the operation is performed
pub logs: Vec<Log>,
/// The time the operation completed.
pub end_ts: Option<I64>,
/// The status of the update
/// - `Queued`
/// - `InProgress`
/// - `Complete`
#[cfg_attr(feature = "mongo", index)]
pub status: UpdateStatus,
/// An optional version on the update, ie build version or deployed version.
#[serde(default, skip_serializing_if = "Version::is_none")]
pub version: Version,
/// An optional commit hash associated with the update, ie cloned hash or deployed hash.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub commit_hash: String,
/// Some unstructured, operation specific data. Not for general usage.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub other_data: String,
/// If the update is for resource config update, give the previous toml contents
#[serde(default, skip_serializing_if = "String::is_empty")]
pub prev_toml: String,
/// If the update is for resource config update, give the current (at time of Update) toml contents
#[serde(default, skip_serializing_if = "String::is_empty")]
pub current_toml: String,
}
impl Update {
pub fn push_simple_log(
&mut self,
stage: &str,
msg: impl Into<String>,
) {
self.logs.push(Log::simple(stage, msg.into()));
}
pub fn push_error_log(
&mut self,
stage: &str,
msg: impl Into<String>,
) {
self.logs.push(Log::error(stage, msg.into()));
}
pub fn in_progress(&mut self) {
self.status = UpdateStatus::InProgress;
}
pub fn finalize(&mut self) {
self.success = all_logs_success(&self.logs);
self.end_ts = Some(komodo_timestamp());
self.status = UpdateStatus::Complete;
}
}
/// Minimal representation of an action performed by Komodo.
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct UpdateListItem {
/// The id of the update
pub id: String,
/// Which operation was run
pub operation: Operation,
/// The starting time of the operation
pub start_ts: I64,
/// Whether the operation was successful
pub success: bool,
/// The username of the user performing update
pub username: String,
/// The user id that triggered the update.
///
/// Also can take these values for operations triggered automatically:
/// - `Procedure`: The operation was triggered as part of a procedure run
/// - `Github`: The operation was triggered by a github webhook
/// - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing.
pub operator: String,
/// The target resource to which this update refers
pub target: ResourceTarget,
/// The status of the update
/// - `Queued`
/// - `InProgress`
/// - `Complete`
pub status: UpdateStatus,
/// An optional version on the update, ie build version or deployed version.
#[serde(default, skip_serializing_if = "Version::is_none")]
pub version: Version,
/// Some unstructured, operation specific data. Not for general usage.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub other_data: String,
}
/// Represents the output of some command being run
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct Log {
/// A label for the log
pub stage: String,
/// The command which was executed
pub command: String,
/// The output of the command in the standard channel
pub stdout: String,
/// The output of the command in the error channel
pub stderr: String,
/// Whether the command run was successful
pub success: bool,
/// The start time of the command execution
pub start_ts: I64,
/// The end time of the command execution
pub end_ts: I64,
}
impl Log {
pub fn simple(stage: &str, msg: String) -> Log {
let ts = unix_timestamp_ms() as i64;
Log {
stage: stage.to_string(),
stdout: msg,
success: true,
start_ts: ts,
end_ts: ts,
..Default::default()
}
}
pub fn error(stage: &str, msg: String) -> Log {
let ts = unix_timestamp_ms() as i64;
Log {
stage: stage.to_string(),
stderr: msg,
start_ts: ts,
end_ts: ts,
success: false,
..Default::default()
}
}
/// Combines stdout / stderr into one log
pub fn combined(&self) -> String {
match (self.stdout.is_empty(), self.stderr.is_empty()) {
(false, false) => {
format!("stdout: {}\n\nstderr: {}", self.stdout, self.stderr)
}
(false, true) => self.stdout.to_string(),
(true, false) => self.stderr.to_string(),
(true, true) => String::from("No log"),
}
}
}
/// An update's status
#[typeshare]
#[derive(
Serialize,
Deserialize,
Debug,
Display,
EnumString,
PartialEq,
Hash,
Eq,
Clone,
Copy,
Default,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum UpdateStatus {
/// The run is in the system but hasn't started yet
Queued,
/// The run is currently running
InProgress,
/// The run is complete
#[default]
Complete,
}