Skip to content

Commit 0481273

Browse files
committed
feat: add upgrade window check for environmentd image ref
This commit introduces a new method to check if the current environmentd image reference is within the upgrade window of the last successful rollout. We default the image ref to the current version if status is not set.
1 parent 2dc5d08 commit 0481273

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

src/cloud-resources/src/crd/materialize.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,36 @@ pub mod v1alpha1 {
479479
}
480480
}
481481

482+
/// Checks if the current environmentd image ref is within the upgrade window of the last
483+
/// successful rollout.
484+
///
485+
/// This check isn't strictly required since environmentd will still be able to determine
486+
/// if the upgrade is allowed or not. However, doing this check allows us to provide
487+
/// the error as soon as possible and in a more user friendly way.
488+
pub fn within_upgrade_window(&self) -> bool {
489+
let current_environmentd_version = self
490+
.status
491+
.as_ref()
492+
.and_then(|status| {
493+
status
494+
.last_completed_rollout_environmentd_image_ref
495+
.as_ref()
496+
})
497+
.and_then(|image_ref| parse_image_ref(image_ref));
498+
499+
if let (Some(new_environmentd_version), Some(current_environmentd_version)) = (
500+
parse_image_ref(&self.spec.environmentd_image_ref),
501+
current_environmentd_version,
502+
) {
503+
// We deny upgrades past 1 major version of the last successful rollout.
504+
new_environmentd_version.major <= current_environmentd_version.major + 1
505+
} else {
506+
// If we couldn't parse either versions, we allow the upgrade since environmentd
507+
// will still be able to determine if the upgrade is allowed or not.
508+
true
509+
}
510+
}
511+
482512
pub fn managed_resource_meta(&self, name: String) -> ObjectMeta {
483513
ObjectMeta {
484514
namespace: Some(self.namespace()),
@@ -516,6 +546,11 @@ pub mod v1alpha1 {
516546
.expect("valid int generation");
517547
}
518548

549+
// Initialize the last completed rollout environmentd image ref to
550+
// the current image ref if not already set.
551+
status.last_completed_rollout_environmentd_image_ref =
552+
Some(self.spec.environmentd_image_ref.clone());
553+
519554
status
520555
})
521556
}
@@ -530,6 +565,10 @@ pub mod v1alpha1 {
530565
pub active_generation: u64,
531566
/// The UUID of the last successfully completed rollout.
532567
pub last_completed_rollout_request: Uuid,
568+
/// The image ref of the environmentd image that was last successfully rolled out.
569+
/// Used to deny upgrades past 1 major version from the last successful rollout.
570+
/// When None, we upgrade anyways.
571+
pub last_completed_rollout_environmentd_image_ref: Option<String>,
533572
/// A hash calculated from the spec of resources to be created based on this Materialize
534573
/// spec. This is used for detecting when the existing resources are up to date.
535574
/// If you want to trigger a rollout without making other changes that would cause this

src/orchestratord/src/controller/materialize.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ impl Context {
360360
MaterializeStatus {
361361
active_generation: desired_generation,
362362
last_completed_rollout_request: mz.requested_reconciliation_id(),
363+
last_completed_rollout_environmentd_image_ref: Some(
364+
mz.spec.environmentd_image_ref.clone(),
365+
),
363366
resource_id: mz.status().resource_id,
364367
resources_hash,
365368
conditions: vec![Condition {
@@ -591,7 +594,7 @@ impl k8s_controller::Context for Context {
591594
)
592595
.await
593596
}
594-
// There are changes pending, and we want to appy them.
597+
// There are changes pending, and we want to apply them.
595598
(false, true, true) => {
596599
// we remove the environment resources hash annotation here
597600
// because if we fail halfway through applying the resources,
@@ -615,6 +618,8 @@ impl k8s_controller::Context for Context {
615618
// we fail later on, we want to ensure that the
616619
// rollout gets retried.
617620
last_completed_rollout_request: status.last_completed_rollout_request,
621+
last_completed_rollout_environmentd_image_ref: status
622+
.last_completed_rollout_environmentd_image_ref,
618623
resource_id: status.resource_id,
619624
resources_hash: String::new(),
620625
conditions: vec![Condition {
@@ -634,6 +639,38 @@ impl k8s_controller::Context for Context {
634639
let mz = &mz;
635640
let status = mz.status();
636641

642+
if !mz.within_upgrade_window() {
643+
let last_completed_rollout_environmentd_image_ref =
644+
status.last_completed_rollout_environmentd_image_ref;
645+
646+
self.update_status(
647+
&mz_api,
648+
mz,
649+
MaterializeStatus {
650+
active_generation,
651+
last_completed_rollout_request: status.last_completed_rollout_request,
652+
last_completed_rollout_environmentd_image_ref: last_completed_rollout_environmentd_image_ref.clone(),
653+
resource_id: status.resource_id,
654+
resources_hash: status.resources_hash,
655+
conditions: vec![Condition {
656+
type_: "UpToDate".into(),
657+
status: "False".into(),
658+
last_transition_time: Time(chrono::offset::Utc::now()),
659+
message: format!(
660+
"Refusing to upgrade from {} to {}. More than one major version from last successful rollout.",
661+
last_completed_rollout_environmentd_image_ref.expect("should be set if upgrade window check fails"),
662+
&mz.spec.environmentd_image_ref,
663+
),
664+
observed_generation: mz.meta().generation,
665+
reason: "FailedDeploy".into(),
666+
}],
667+
},
668+
active_generation != desired_generation,
669+
)
670+
.await?;
671+
return Ok(None);
672+
}
673+
637674
if mz.spec.rollout_strategy
638675
== MaterializeRolloutStrategy::ImmediatelyPromoteCausingDowntime
639676
{
@@ -673,6 +710,8 @@ impl k8s_controller::Context for Context {
673710
// rollout gets retried.
674711
last_completed_rollout_request: status
675712
.last_completed_rollout_request,
713+
last_completed_rollout_environmentd_image_ref: status
714+
.last_completed_rollout_environmentd_image_ref,
676715
resource_id: status.resource_id,
677716
resources_hash: resources_hash.clone(),
678717
conditions: vec![Condition {
@@ -710,6 +749,8 @@ impl k8s_controller::Context for Context {
710749
// the rollout and we want to ensure it gets
711750
// retried.
712751
last_completed_rollout_request: status.last_completed_rollout_request,
752+
last_completed_rollout_environmentd_image_ref: status
753+
.last_completed_rollout_environmentd_image_ref,
713754
resource_id: status.resource_id,
714755
resources_hash: status.resources_hash,
715756
conditions: vec![Condition {
@@ -746,6 +787,8 @@ impl k8s_controller::Context for Context {
746787
MaterializeStatus {
747788
active_generation,
748789
last_completed_rollout_request: mz.requested_reconciliation_id(),
790+
last_completed_rollout_environmentd_image_ref: status
791+
.last_completed_rollout_environmentd_image_ref,
749792
resource_id: status.resource_id,
750793
resources_hash: status.resources_hash,
751794
conditions: vec![Condition {
@@ -786,6 +829,8 @@ impl k8s_controller::Context for Context {
786829
MaterializeStatus {
787830
active_generation,
788831
last_completed_rollout_request: mz.requested_reconciliation_id(),
832+
last_completed_rollout_environmentd_image_ref: status
833+
.last_completed_rollout_environmentd_image_ref,
789834
resource_id: status.resource_id,
790835
resources_hash: status.resources_hash,
791836
conditions: vec![Condition {

0 commit comments

Comments
 (0)