6
6
//! updates.
7
7
8
8
use super :: MgsClients ;
9
- use super :: UpdateProgress ;
10
9
use crate :: host_phase1_updater:: ReconfiguratorHostPhase1Updater ;
11
10
use crate :: mgs_clients:: GatewaySpComponentResetError ;
12
11
use crate :: mgs_clients:: RetryableMgsError ;
@@ -15,18 +14,15 @@ use crate::rot_updater::ReconfiguratorRotUpdater;
15
14
use crate :: sp_updater:: ReconfiguratorSpUpdater ;
16
15
use futures:: future:: BoxFuture ;
17
16
use gateway_client:: types:: SpType ;
18
- use gateway_client:: types:: SpUpdateStatus ;
19
17
use gateway_types:: rot:: RotSlot ;
20
18
use nexus_types:: deployment:: ExpectedVersion ;
21
19
use nexus_types:: deployment:: PendingMgsUpdate ;
22
20
use nexus_types:: deployment:: PendingMgsUpdateDetails ;
23
21
use omicron_common:: disk:: M2Slot ;
24
- use slog:: Logger ;
25
- use slog:: { debug, error, info, warn} ;
22
+ use slog:: error;
26
23
use std:: net:: SocketAddrV6 ;
27
24
use std:: time:: Duration ;
28
25
use thiserror:: Error ;
29
- use tokio:: sync:: watch;
30
26
use tufaceous_artifact:: ArtifactHash ;
31
27
use tufaceous_artifact:: ArtifactKind ;
32
28
use tufaceous_artifact:: ArtifactVersion ;
@@ -69,202 +65,6 @@ pub enum SpComponentUpdateError {
69
65
UpdateFailedWithMessage ( String ) ,
70
66
}
71
67
72
- /// Describes an update to a component for which the SP drives the update
73
- ///
74
- /// This trait is essentially historical at this point. We maintain impls so
75
- /// that we have tested reference implementations. But these will eventually be
76
- /// migrated to `ReconfiguratorSpComponentUpdater` instead.
77
- pub trait SpComponentUpdater {
78
- /// The target component.
79
- ///
80
- /// Should be produced via `SpComponent::const_as_str()`.
81
- fn component ( & self ) -> & ' static str ;
82
-
83
- /// The type of the target SP.
84
- fn target_sp_type ( & self ) -> SpType ;
85
-
86
- /// The slot number of the target SP.
87
- fn target_sp_slot ( & self ) -> u16 ;
88
-
89
- /// The target firmware slot for the component.
90
- fn firmware_slot ( & self ) -> u16 ;
91
-
92
- /// The ID of this update.
93
- fn update_id ( & self ) -> Uuid ;
94
-
95
- /// The update payload data to send to MGS.
96
- // TODO-performance This has to be convertible into a `reqwest::Body`, so we
97
- // return an owned Vec. That requires all our implementors to clone the data
98
- // at least once; maybe we should use `Bytes` instead (which is cheap to
99
- // clone and also convertible into a reqwest::Body)?
100
- fn update_data ( & self ) -> Vec < u8 > ;
101
-
102
- /// The sending half of the watch channel to report update progress.
103
- fn progress ( & self ) -> & watch:: Sender < Option < UpdateProgress > > ;
104
-
105
- /// Logger to use while performing this update.
106
- fn logger ( & self ) -> & Logger ;
107
- }
108
-
109
- pub ( super ) async fn deliver_update (
110
- updater : & ( dyn SpComponentUpdater + Send + Sync ) ,
111
- mgs_clients : & mut MgsClients ,
112
- ) -> Result < ( ) , SpComponentUpdateError > {
113
- // Start the update.
114
- mgs_clients
115
- . try_all_serially ( updater. logger ( ) , |client| async move {
116
- client
117
- . sp_component_update (
118
- updater. target_sp_type ( ) ,
119
- updater. target_sp_slot ( ) ,
120
- updater. component ( ) ,
121
- updater. firmware_slot ( ) ,
122
- & updater. update_id ( ) ,
123
- reqwest:: Body :: from ( updater. update_data ( ) ) ,
124
- )
125
- . await ?;
126
- updater. progress ( ) . send_replace ( Some ( UpdateProgress :: Started ) ) ;
127
- info ! (
128
- updater. logger( ) , "update started" ;
129
- "mgs_addr" => client. baseurl( ) ,
130
- ) ;
131
- Ok :: < _ , GatewayClientError > ( ( ) )
132
- } )
133
- . await ?;
134
-
135
- // Wait for the update to complete.
136
- loop {
137
- let status = mgs_clients
138
- . try_all_serially ( updater. logger ( ) , |client| async move {
139
- let update_status = client
140
- . sp_component_update_status (
141
- updater. target_sp_type ( ) ,
142
- updater. target_sp_slot ( ) ,
143
- updater. component ( ) ,
144
- )
145
- . await ?;
146
-
147
- debug ! (
148
- updater. logger( ) , "got update status" ;
149
- "mgs_addr" => client. baseurl( ) ,
150
- "status" => ?update_status,
151
- ) ;
152
-
153
- Ok :: < _ , GatewayClientError > ( update_status)
154
- } )
155
- . await ?;
156
-
157
- if status_is_complete (
158
- status. into_inner ( ) ,
159
- updater. update_id ( ) ,
160
- updater. progress ( ) ,
161
- updater. logger ( ) ,
162
- ) ? {
163
- updater. progress ( ) . send_replace ( Some ( UpdateProgress :: InProgress {
164
- progress : Some ( 1.0 ) ,
165
- } ) ) ;
166
- return Ok ( ( ) ) ;
167
- }
168
-
169
- tokio:: time:: sleep ( STATUS_POLL_INTERVAL ) . await ;
170
- }
171
- }
172
-
173
- fn status_is_complete (
174
- status : SpUpdateStatus ,
175
- update_id : Uuid ,
176
- progress_tx : & watch:: Sender < Option < UpdateProgress > > ,
177
- log : & Logger ,
178
- ) -> Result < bool , SpComponentUpdateError > {
179
- match status {
180
- // For `Preparing` and `InProgress`, we could check the progress
181
- // information returned by these steps and try to check that
182
- // we're still _making_ progress, but every Nexus instance needs
183
- // to do that anyway in case we (or the MGS instance delivering
184
- // the update) crash, so we'll omit that check here. Instead, we
185
- // just sleep and we'll poll again shortly.
186
- SpUpdateStatus :: Preparing { id, progress } => {
187
- if id == update_id {
188
- let progress = progress. and_then ( |progress| {
189
- if progress. current > progress. total {
190
- warn ! (
191
- log, "nonsense preparing progress" ;
192
- "current" => progress. current,
193
- "total" => progress. total,
194
- ) ;
195
- None
196
- } else if progress. total == 0 {
197
- None
198
- } else {
199
- Some (
200
- f64:: from ( progress. current )
201
- / f64:: from ( progress. total ) ,
202
- )
203
- }
204
- } ) ;
205
- progress_tx
206
- . send_replace ( Some ( UpdateProgress :: Preparing { progress } ) ) ;
207
- Ok ( false )
208
- } else {
209
- Err ( SpComponentUpdateError :: DifferentUpdatePreparing ( id) )
210
- }
211
- }
212
- SpUpdateStatus :: InProgress { id, bytes_received, total_bytes } => {
213
- if id == update_id {
214
- let progress = if bytes_received > total_bytes {
215
- warn ! (
216
- log, "nonsense update progress" ;
217
- "bytes_received" => bytes_received,
218
- "total_bytes" => total_bytes,
219
- ) ;
220
- None
221
- } else if total_bytes == 0 {
222
- None
223
- } else {
224
- Some ( f64:: from ( bytes_received) / f64:: from ( total_bytes) )
225
- } ;
226
- progress_tx. send_replace ( Some ( UpdateProgress :: InProgress {
227
- progress,
228
- } ) ) ;
229
- Ok ( false )
230
- } else {
231
- Err ( SpComponentUpdateError :: DifferentUpdateInProgress ( id) )
232
- }
233
- }
234
- SpUpdateStatus :: Complete { id } => {
235
- if id == update_id {
236
- Ok ( true )
237
- } else {
238
- Err ( SpComponentUpdateError :: DifferentUpdateComplete ( id) )
239
- }
240
- }
241
- SpUpdateStatus :: None => Err ( SpComponentUpdateError :: UpdateStatusLost ) ,
242
- SpUpdateStatus :: Aborted { id } => {
243
- if id == update_id {
244
- Err ( SpComponentUpdateError :: UpdateAborted )
245
- } else {
246
- Err ( SpComponentUpdateError :: DifferentUpdateAborted ( id) )
247
- }
248
- }
249
- SpUpdateStatus :: Failed { code, id } => {
250
- if id == update_id {
251
- Err ( SpComponentUpdateError :: UpdateFailedWithCode ( code) )
252
- } else {
253
- Err ( SpComponentUpdateError :: DifferentUpdateFailed ( id) )
254
- }
255
- }
256
- SpUpdateStatus :: RotError { id, message } => {
257
- if id == update_id {
258
- Err ( SpComponentUpdateError :: UpdateFailedWithMessage ( format ! (
259
- "rot error: {message}"
260
- ) ) )
261
- } else {
262
- Err ( SpComponentUpdateError :: DifferentUpdateFailed ( id) )
263
- }
264
- }
265
- }
266
- }
267
-
268
68
/// Implementors provide helper functions used while updating a particular SP
269
69
/// component
270
70
///
0 commit comments