Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions eksup/src/playbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,20 @@ pub(crate) fn create(
fs::write(filename, rendered)?;
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn release_data_covers_every_supported_target() {
let data = get_release_data().unwrap();
for minor in (version::MINIMUM + 1)..=version::LATEST {
let target = version::format_version(minor);
assert!(
data.contains_key(&target),
"data.yaml is missing release data for {target}"
);
}
}
}
2 changes: 1 addition & 1 deletion eksup/src/version.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::{Context, Result, bail};

/// Minimum supported minor version — clusters below this cannot be analyzed
pub const MINIMUM: i32 = 30;
pub const MINIMUM: i32 = 32;

/// Latest supported minor version
pub const LATEST: i32 = 35;
Expand Down
10 changes: 0 additions & 10 deletions eksup/templates/data.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
'1.30':
release_url: https://kubernetes.io/blog/2024/04/17/kubernetes-v1-30-release/

'1.31':
release_url: https://kubernetes.io/blog/2024/08/13/kubernetes-v1-31-release/

'1.32':
release_url: https://kubernetes.io/blog/2024/12/11/kubernetes-v1-32-release/
deprecation_url: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-32

'1.33':
release_url: https://kubernetes.io/blog/2025/04/23/kubernetes-v1-33-release/

Expand Down
2 changes: 1 addition & 1 deletion eksup/tests/common/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn healthy_aws() -> MockAwsClients {
MockAwsClients {
cluster: Cluster::builder()
.name("test-cluster")
.version("1.30")
.version("1.32")
.health(ClusterHealth::builder().build())
.resources_vpc_config(
VpcConfigResponse::builder()
Expand Down
2 changes: 1 addition & 1 deletion eksup/tests/common/mock_aws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub struct MockAwsClients {
impl Default for MockAwsClients {
fn default() -> Self {
Self {
cluster: Cluster::builder().name("test-cluster").version("1.30").build(),
cluster: Cluster::builder().name("test-cluster").version("1.32").build(),
subnet_ips: vec![],
addons: vec![],
addon_versions: HashMap::new(),
Expand Down
39 changes: 11 additions & 28 deletions eksup/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ async fn snapshot_workload_issues_json() {
async fn snapshot_version_skew_text() {
let aws = fixtures::healthy_aws();
let k8s = MockK8sClients {
nodes: vec![fixtures::make_node("node-1", 28), fixtures::make_node("node-2", 27)],
nodes: vec![fixtures::make_node("node-1", 30), fixtures::make_node("node-2", 29)],
..Default::default()
};
let results = run_analysis(&aws, &k8s).await;
Expand Down Expand Up @@ -140,7 +140,7 @@ async fn snapshot_mixed_findings_text() {
},
];
let k8s = MockK8sClients {
nodes: vec![fixtures::make_node("node-1", 28)],
nodes: vec![fixtures::make_node("node-1", 30)],
resources: vec![fixtures::make_deployment("web", "default", 1)],
..Default::default()
};
Expand All @@ -166,7 +166,7 @@ async fn snapshot_mixed_findings_json() {
},
];
let k8s = MockK8sClients {
nodes: vec![fixtures::make_node("node-1", 28)],
nodes: vec![fixtures::make_node("node-1", 30)],
resources: vec![fixtures::make_deployment("web", "default", 1)],
..Default::default()
};
Expand All @@ -184,7 +184,7 @@ async fn snapshot_mixed_findings_json() {
async fn snapshot_filter_recommended_text() {
let aws = fixtures::healthy_aws();
let k8s = MockK8sClients {
nodes: vec![fixtures::make_node("node-1", 29)],
nodes: vec![fixtures::make_node("node-1", 31)],
resources: vec![fixtures::make_deployment("web", "default", 2)],
..Default::default()
};
Expand Down Expand Up @@ -271,7 +271,7 @@ async fn snapshot_playbook_mixed() {
aws.nodegroups = vec![Nodegroup::builder().nodegroup_name("mng-1").build()];
aws.fargate_profiles = vec![FargateProfile::builder().fargate_profile_name("fp-1").build()];
let k8s = MockK8sClients {
nodes: vec![fixtures::make_node("node-1", 28)],
nodes: vec![fixtures::make_node("node-1", 30)],
resources: vec![fixtures::make_deployment("web", "default", 1)],
..Default::default()
};
Expand Down Expand Up @@ -351,8 +351,8 @@ async fn playbook_template_variables_populated() {

assert!(output.contains("test-cluster"), "cluster name should appear");
assert!(output.contains("us-east-1"), "region should appear");
assert!(output.contains("v1.30"), "current version should appear");
assert!(output.contains("v1.31"), "target version should appear");
assert!(output.contains("v1.32"), "current version should appear");
assert!(output.contains("v1.33"), "target version should appear");
assert!(output.contains("kubernetes.io/blog/"), "release URL should appear");
}

Expand All @@ -374,31 +374,14 @@ async fn playbook_pod_ips_healthy_when_no_custom_networking() {
}

#[tokio::test]
async fn playbook_deprecation_url_absent_for_1_31_target() {
// Cluster at 1.30 -> target 1.31 (no deprecation_url in data.yaml)
let aws = aws_at_version("1.30");
async fn playbook_deprecation_url_absent_for_1_33_target() {
// Cluster at 1.32 -> target 1.33 (no deprecation_url in data.yaml)
let aws = aws_at_version("1.32");
let k8s = fixtures::healthy_k8s();
let output = render_playbook(&aws, &k8s).await;

assert!(
!output.contains("API deprecations"),
"deprecation link should be absent for 1.31 target"
);
}

#[tokio::test]
async fn playbook_deprecation_url_present_for_1_32_target() {
// Cluster at 1.31 -> target 1.32 (has deprecation_url in data.yaml)
let aws = aws_at_version("1.31");
let k8s = fixtures::healthy_k8s();
let output = render_playbook(&aws, &k8s).await;

assert!(
output.contains("API deprecations"),
"deprecation link should be present for 1.32 target"
);
assert!(
output.contains("deprecation-guide/#v1-32"),
"deprecation URL should point to 1.32 guide"
"deprecation link should be absent for 1.33 target"
);
}
50 changes: 25 additions & 25 deletions eksup/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async fn subnet_findings_insufficient_control_plane_ips() {
#[tokio::test]
async fn addon_findings_no_addons() {
let aws = fixtures::healthy_aws();
let result = eksup::eks::get_addon_findings(&aws, "test-cluster", "1.30", 31)
let result = eksup::eks::get_addon_findings(&aws, "test-cluster", "1.32", 33)
.await
.unwrap();
assert!(result.version_compatibility.is_empty());
Expand All @@ -99,16 +99,16 @@ async fn addon_findings_version_incompatible() {
aws.addons = vec![Addon::builder().addon_name("vpc-cni").addon_version("v1.12.0").build()];
aws.addon_versions = HashMap::from([
(
("vpc-cni".into(), "1.30".into()),
("vpc-cni".into(), "1.32".into()),
fixtures::make_addon_version("v1.15.0", "v1.14.0", &["v1.15.0", "v1.14.0"]),
),
(
("vpc-cni".into(), "1.31".into()),
("vpc-cni".into(), "1.33".into()),
fixtures::make_addon_version("v1.16.0", "v1.15.0", &["v1.16.0", "v1.15.0"]),
),
]);

let result = eksup::eks::get_addon_findings(&aws, "test-cluster", "1.30", 31)
let result = eksup::eks::get_addon_findings(&aws, "test-cluster", "1.32", 33)
.await
.unwrap();
assert_eq!(result.version_compatibility.len(), 1);
Expand All @@ -121,7 +121,7 @@ async fn addon_findings_version_incompatible() {
#[tokio::test]
async fn data_plane_findings_empty() {
let aws = fixtures::healthy_aws();
let result = eksup::eks::get_data_plane_findings(&aws, &aws.cluster, 31)
let result = eksup::eks::get_data_plane_findings(&aws, &aws.cluster, 33)
.await
.unwrap();
assert!(result.eks_managed_nodegroup_health.is_empty());
Expand Down Expand Up @@ -154,7 +154,7 @@ async fn data_plane_findings_node_ips() {
},
];

let result = eksup::eks::get_data_plane_findings(&aws, &aws.cluster, 31)
let result = eksup::eks::get_data_plane_findings(&aws, &aws.cluster, 33)
.await
.unwrap();
assert!(!result.node_ips.is_empty(), "low IP subnets should produce findings");
Expand All @@ -167,7 +167,7 @@ async fn data_plane_findings_node_ips() {
#[tokio::test]
async fn kubernetes_findings_empty() {
let k8s = fixtures::healthy_k8s();
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &ChecksConfig::default())
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &ChecksConfig::default())
.await
.unwrap();
assert!(result.version_skew.is_empty());
Expand All @@ -177,11 +177,11 @@ async fn kubernetes_findings_empty() {
#[tokio::test]
async fn kubernetes_findings_version_skew() {
let k8s = MockK8sClients {
nodes: vec![fixtures::make_node("node-1", 28)],
nodes: vec![fixtures::make_node("node-1", 30)],
..Default::default()
};

let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &ChecksConfig::default())
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &ChecksConfig::default())
.await
.unwrap();
assert_eq!(result.version_skew.len(), 1);
Expand All @@ -194,7 +194,7 @@ async fn kubernetes_findings_workload_issues() {
..Default::default()
};

let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &ChecksConfig::default())
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &ChecksConfig::default())
.await
.unwrap();
assert!(!result.min_replicas.is_empty(), "1 replica should trigger finding");
Expand Down Expand Up @@ -244,7 +244,7 @@ async fn kubernetes_findings_missing_pdb() {
..Default::default()
};

let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &ChecksConfig::default())
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &ChecksConfig::default())
.await
.unwrap();
assert!(
Expand All @@ -261,7 +261,7 @@ async fn kubernetes_findings_missing_pdb() {
async fn analyze_healthy_cluster() {
let aws = fixtures::healthy_aws();
let k8s = fixtures::healthy_k8s();
let results = eksup::analysis::analyze(&aws, &k8s, &aws.cluster, 31, &Config::default())
let results = eksup::analysis::analyze(&aws, &k8s, &aws.cluster, 33, &Config::default())
.await
.unwrap();

Expand All @@ -275,12 +275,12 @@ async fn analyze_healthy_cluster() {
async fn analyze_filter_recommended() {
let k8s = MockK8sClients {
resources: vec![fixtures::make_deployment("web", "default", 2)],
nodes: vec![fixtures::make_node("node-1", 29)],
nodes: vec![fixtures::make_node("node-1", 31)],
..Default::default()
};
let aws = fixtures::healthy_aws();

let mut results = eksup::analysis::analyze(&aws, &k8s, &aws.cluster, 31, &Config::default())
let mut results = eksup::analysis::analyze(&aws, &k8s, &aws.cluster, 33, &Config::default())
.await
.unwrap();
let before_skew = results.kubernetes.version_skew.len();
Expand All @@ -295,11 +295,11 @@ async fn analyze_filter_recommended() {

#[tokio::test]
async fn analyze_with_explicit_target() {
let aws = fixtures::healthy_aws(); // cluster version "1.30"
let aws = fixtures::healthy_aws(); // cluster version "1.32"
let k8s = fixtures::healthy_k8s();

// Jump from 1.30 → 1.33
let results = eksup::analysis::analyze(&aws, &k8s, &aws.cluster, 33, &Config::default())
// Jump from 1.32 → 1.35
let results = eksup::analysis::analyze(&aws, &k8s, &aws.cluster, 35, &Config::default())
.await
.unwrap();

Expand Down Expand Up @@ -479,14 +479,14 @@ async fn analyze_aws_error_propagates() {

let cluster = aws_sdk_eks::types::Cluster::builder()
.name("test")
.version("1.30")
.version("1.32")
.build();

let result = eksup::analysis::analyze(
&MockAwsClientsError,
&MockK8sClientsError,
&cluster,
31,
33,
&Config::default(),
)
.await;
Expand All @@ -504,7 +504,7 @@ async fn kubernetes_findings_min_replicas_default_threshold() {
resources: vec![fixtures::make_deployment("web", "default", 2)],
..Default::default()
};
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &ChecksConfig::default())
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &ChecksConfig::default())
.await
.unwrap();
assert!(
Expand All @@ -525,7 +525,7 @@ async fn kubernetes_findings_min_replicas_strict_threshold() {
min_replicas: 3,
..Default::default()
};
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &checks)
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &checks)
.await
.unwrap();
assert_eq!(
Expand All @@ -549,7 +549,7 @@ async fn kubernetes_findings_min_replicas_ignored() {
}],
..Default::default()
};
let (result, suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &checks)
let (result, suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &checks)
.await
.unwrap();
assert!(
Expand Down Expand Up @@ -581,7 +581,7 @@ async fn kubernetes_findings_min_replicas_per_workload_override() {
}],
..Default::default()
};
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &checks)
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &checks)
.await
.unwrap();
assert_eq!(result.min_replicas.len(), 1, "only etcd should fail (3 < 5)");
Expand Down Expand Up @@ -633,7 +633,7 @@ async fn kubernetes_findings_pdb_ignored() {
};

// Without ignore: should trigger PDB finding
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &ChecksConfig::default())
let (result, _suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &ChecksConfig::default())
.await
.unwrap();
assert!(
Expand All @@ -649,7 +649,7 @@ async fn kubernetes_findings_pdb_ignored() {
namespace: "default".into(),
}],
};
let (result, suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 30, 31, &checks)
let (result, suppressed) = eksup::k8s::get_kubernetes_findings(&k8s, 32, 33, &checks)
.await
.unwrap();
assert!(
Expand Down
7 changes: 4 additions & 3 deletions eksup/tests/snapshots/e2e__mixed_findings_json.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: eksup/tests/e2e.rs
assertion_line: 176
expression: output
---
{
Expand Down Expand Up @@ -52,9 +53,9 @@ expression: output
"remediation": "Recommended"
},
"name": "node-1",
"kubelet_version": "v1.28.0",
"kubernetes_version": "v1.28",
"control_plane_version": "v1.30",
"kubelet_version": "v1.30.0",
"kubernetes_version": "v1.30",
"control_plane_version": "v1.32",
"version_skew": "+2"
}
],
Expand Down
3 changes: 2 additions & 1 deletion eksup/tests/snapshots/e2e__mixed_findings_text.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: eksup/tests/e2e.rs
assertion_line: 150
expression: output
---
┌────────┬────┬──────────┬──────────────┐
Expand All @@ -11,7 +12,7 @@ expression: output
┌────────┬────┬────────┬───────┬───────────────┬──────┐
│ CHECK │ │ NAME │ NODE │ CONTROL PLANE │ SKEW │
├────────┼────┼────────┼───────┼───────────────┼──────┤
│ K8S001 │ ⚠️ │ node-1 │ v1.28 │ v1.30 │ +2 │
│ K8S001 │ ⚠️ │ node-1 │ v1.30 │ v1.32 │ +2 │
└────────┴────┴────────┴───────┴───────────────┴──────┘
┌────────┬────┬──────┬───────────┬────────────┬──────────┐
│ CHECK │ │ NAME │ NAMESPACE │ KIND │ REPLICAS │
Expand Down
Loading
Loading