299 lines
12 KiB
Java
299 lines
12 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* Kubernetes0007Algorithm — CWE-407 unit test for kubernetes-0007
|
||
*
|
||
* kubernetes-0007: pod_failure_policy.go:126,128
|
||
* for _, containerStatus := range containerStatuses { // O(C) containers
|
||
* isOnExitCodesOperatorMatching(exitCode, requirement) →
|
||
* slices.Contains(requirement.Values, exitCode) // O(V) scan
|
||
* }
|
||
*
|
||
* Called from matchPodFailurePolicy() for every failed pod:
|
||
* for _, p := range failedPods { matchPodFailurePolicy(policy, p) }
|
||
*
|
||
* Total complexity: O(P × R × C × V) — pods × rules × containers × values
|
||
*
|
||
* SLOW: slices.Contains([]int32, exitCode) — O(V) linear scan per container
|
||
* FAST: map[int32]struct{} pre-built once per rule — O(1) per container
|
||
*
|
||
* No JUnit. Run: javac -d . Kubernetes0007Algorithm.java && java -ea unit.Kubernetes0007Algorithm
|
||
*/
|
||
public class Kubernetes0007Algorithm {
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Data model — mirrors PodFailurePolicyOnExitCodesRequirement
|
||
// -------------------------------------------------------------------------
|
||
|
||
static class ExitCodeRequirement {
|
||
final boolean opIn; // true=In, false=NotIn
|
||
final int[] values; // requirement.Values []int32
|
||
|
||
ExitCodeRequirement(boolean opIn, int[] values) {
|
||
this.opIn = opIn;
|
||
this.values = values;
|
||
}
|
||
}
|
||
|
||
static class PolicyRule {
|
||
final ExitCodeRequirement requirement;
|
||
PolicyRule(ExitCodeRequirement r) { this.requirement = r; }
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Op counters for measuring linear vs map scans
|
||
// -------------------------------------------------------------------------
|
||
|
||
static long slowOps = 0;
|
||
static long fastOps = 0;
|
||
|
||
// -------------------------------------------------------------------------
|
||
// SLOW: O(V) linear scan — models slices.Contains(requirement.Values, exitCode)
|
||
// -------------------------------------------------------------------------
|
||
|
||
static boolean isMatchingSlow(int exitCode, ExitCodeRequirement req) {
|
||
for (int v : req.values) { // O(V) linear scan
|
||
slowOps++;
|
||
if (v == exitCode) {
|
||
return req.opIn; // In: found → match
|
||
}
|
||
}
|
||
return !req.opIn; // In: not found → no match; NotIn: not found → match
|
||
}
|
||
|
||
/**
|
||
* Match one pod's containers against all policy rules.
|
||
* Models matchPodFailurePolicy() + getMatchingContainerFromList().
|
||
* Returns true if any rule matches any container exit code.
|
||
*/
|
||
static boolean matchPodSlow(int[] containerExitCodes, List<PolicyRule> rules) {
|
||
for (PolicyRule rule : rules) { // O(R) rules
|
||
for (int exitCode : containerExitCodes) { // O(C) containers
|
||
if (exitCode != 0 && isMatchingSlow(exitCode, rule.requirement)) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Run all failed pods through policy matching.
|
||
* Models nonIgnoredFailedPodsCount() hot path.
|
||
*/
|
||
static int countIgnoredPodsSlow(int[][] failedPods, List<PolicyRule> rules) {
|
||
int ignored = 0;
|
||
for (int[] podExitCodes : failedPods) { // O(P) pods
|
||
if (matchPodSlow(podExitCodes, rules)) {
|
||
ignored++;
|
||
}
|
||
}
|
||
return ignored;
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// FAST: pre-build map[int32]struct{} once per rule — O(1) per container
|
||
// -------------------------------------------------------------------------
|
||
|
||
static boolean isMatchingFast(int exitCode, boolean opIn, Set<Integer> valueSet) {
|
||
fastOps++;
|
||
boolean found = valueSet.contains(exitCode); // O(1) hash lookup
|
||
return opIn ? found : !found;
|
||
}
|
||
|
||
static boolean matchPodFast(int[] containerExitCodes, List<PolicyRule> rules,
|
||
List<Set<Integer>> ruleSets) {
|
||
for (int i = 0; i < rules.size(); i++) { // O(R)
|
||
PolicyRule rule = rules.get(i);
|
||
Set<Integer> valueSet = ruleSets.get(i);
|
||
for (int exitCode : containerExitCodes) { // O(C)
|
||
if (exitCode != 0 && isMatchingFast(exitCode, rule.requirement.opIn, valueSet)) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static int countIgnoredPodsFast(int[][] failedPods, List<PolicyRule> rules) {
|
||
// Pre-build one set per rule — O(R × V) one-time cost
|
||
List<Set<Integer>> ruleSets = new ArrayList<>(rules.size());
|
||
for (PolicyRule rule : rules) {
|
||
Set<Integer> set = new HashSet<>(rule.requirement.values.length * 2);
|
||
for (int v : rule.requirement.values) { set.add(v); }
|
||
ruleSets.add(set);
|
||
}
|
||
// Now match all pods — O(P × R × C × 1)
|
||
int ignored = 0;
|
||
for (int[] podExitCodes : failedPods) {
|
||
if (matchPodFast(podExitCodes, rules, ruleSets)) {
|
||
ignored++;
|
||
}
|
||
}
|
||
return ignored;
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Helpers
|
||
// -------------------------------------------------------------------------
|
||
|
||
/** Build R rules each with V exit code values (some matching exitSeed) */
|
||
static List<PolicyRule> buildRules(int R, int V, int exitSeed) {
|
||
List<PolicyRule> rules = new ArrayList<>(R);
|
||
for (int i = 0; i < R; i++) {
|
||
int[] values = new int[V];
|
||
for (int j = 0; j < V; j++) {
|
||
values[j] = (exitSeed + i * V + j) % 256 + 1;
|
||
}
|
||
rules.add(new PolicyRule(new ExitCodeRequirement(true, values)));
|
||
}
|
||
return rules;
|
||
}
|
||
|
||
/** Build P pods each with C containers, exit codes spread to hit last rule */
|
||
static int[][] buildPods(int P, int C) {
|
||
int[][] pods = new int[P][C];
|
||
for (int i = 0; i < P; i++) {
|
||
for (int j = 0; j < C; j++) {
|
||
// Use exit code that is unlikely to match early rules → worst case scan
|
||
pods[i][j] = (i * C + j) % 200 + 50;
|
||
}
|
||
}
|
||
return pods;
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Tests
|
||
// -------------------------------------------------------------------------
|
||
|
||
static void testCorrectness() {
|
||
// Single rule: In [1, 2, 3]
|
||
List<PolicyRule> rules = new ArrayList<>();
|
||
rules.add(new PolicyRule(new ExitCodeRequirement(true, new int[]{1, 2, 3})));
|
||
|
||
int[][] matchingPod = {{1, 0}}; // exit code 1 → should match
|
||
int[][] noMatchPod = {{5, 0}}; // exit code 5 → no match
|
||
|
||
List<Set<Integer>> sets = new ArrayList<>();
|
||
sets.add(new HashSet<>(Arrays.asList(1, 2, 3)));
|
||
|
||
assert countIgnoredPodsSlow(matchingPod, rules) == 1
|
||
: "slow: expected match";
|
||
assert countIgnoredPodsFast(matchingPod, rules) == 1
|
||
: "fast: expected match";
|
||
assert countIgnoredPodsSlow(noMatchPod, rules) == 0
|
||
: "slow: expected no match";
|
||
assert countIgnoredPodsFast(noMatchPod, rules) == 0
|
||
: "fast: expected no match";
|
||
|
||
// NotIn rule: NotIn [1,2,3] — exit code 5 should match (5 not in set)
|
||
List<PolicyRule> notInRules = new ArrayList<>();
|
||
notInRules.add(new PolicyRule(new ExitCodeRequirement(false, new int[]{1, 2, 3})));
|
||
assert countIgnoredPodsSlow(noMatchPod, notInRules) == 1
|
||
: "slow NotIn: expected match for code 5";
|
||
assert countIgnoredPodsFast(noMatchPod, notInRules) == 1
|
||
: "fast NotIn: expected match for code 5";
|
||
|
||
System.out.println("PASS correctness: In/NotIn matching verified");
|
||
}
|
||
|
||
static void testOpsCount() {
|
||
int P = 100, R = 10, C = 3, V = 20;
|
||
List<PolicyRule> rules = buildRules(R, V, 200); // high exit codes → no early match
|
||
int[][] pods = buildPods(P, C);
|
||
|
||
slowOps = 0;
|
||
fastOps = 0;
|
||
countIgnoredPodsSlow(pods, rules);
|
||
countIgnoredPodsFast(pods, rules);
|
||
|
||
long expectedSlowBound = (long) P * R * C * V;
|
||
long expectedFastBound = (long) P * R * C;
|
||
|
||
System.out.printf("PASS ops_count P=%d R=%d C=%d V=%d: slowOps=%d fastOps=%d ratio=%.0fx%n",
|
||
P, R, C, V, slowOps, fastOps, (double) slowOps / Math.max(fastOps, 1));
|
||
assert slowOps >= fastOps * 5 :
|
||
"expected slowOps >> fastOps, got slowOps=" + slowOps + " fastOps=" + fastOps;
|
||
}
|
||
|
||
static void testPerf_P500_R15_C3_V30() {
|
||
int P = 500, R = 15, C = 3, V = 30;
|
||
List<PolicyRule> rules = buildRules(R, V, 210);
|
||
int[][] pods = buildPods(P, C);
|
||
|
||
// Warm up JVM
|
||
for (int i = 0; i < 20; i++) {
|
||
countIgnoredPodsSlow(pods, rules);
|
||
countIgnoredPodsFast(pods, rules);
|
||
}
|
||
|
||
long t0 = System.nanoTime();
|
||
int slowResult = 0;
|
||
for (int i = 0; i < 500; i++) {
|
||
slowResult += countIgnoredPodsSlow(pods, rules);
|
||
}
|
||
long slowNs = System.nanoTime() - t0;
|
||
|
||
long t1 = System.nanoTime();
|
||
int fastResult = 0;
|
||
for (int i = 0; i < 500; i++) {
|
||
fastResult += countIgnoredPodsFast(pods, rules);
|
||
}
|
||
long fastNs = System.nanoTime() - t1;
|
||
|
||
assert slowResult == fastResult : "results differ: " + slowResult + " vs " + fastResult;
|
||
double ratio = (double) slowNs / Math.max(fastNs, 1);
|
||
System.out.printf("PASS perf P=%d R=%d C=%d V=%d 500x: slow=%dms fast=%dms ratio=%.1fx%n",
|
||
P, R, C, V, slowNs / 1_000_000, fastNs / 1_000_000, ratio);
|
||
assert ratio >= 0.5 :
|
||
"ratio too low: " + ratio + " (slow=" + slowNs + "ns fast=" + fastNs + "ns)";
|
||
}
|
||
|
||
static void testPerf_P2000_R20_C5_V10_stress() {
|
||
int P = 2000, R = 20, C = 5, V = 10;
|
||
List<PolicyRule> rules = buildRules(R, V, 220);
|
||
int[][] pods = buildPods(P, C);
|
||
|
||
// Warm up
|
||
for (int i = 0; i < 10; i++) {
|
||
countIgnoredPodsSlow(pods, rules);
|
||
countIgnoredPodsFast(pods, rules);
|
||
}
|
||
|
||
long t0 = System.nanoTime();
|
||
int slowResult = 0;
|
||
for (int i = 0; i < 100; i++) {
|
||
slowResult += countIgnoredPodsSlow(pods, rules);
|
||
}
|
||
long slowNs = System.nanoTime() - t0;
|
||
|
||
long t1 = System.nanoTime();
|
||
int fastResult = 0;
|
||
for (int i = 0; i < 100; i++) {
|
||
fastResult += countIgnoredPodsFast(pods, rules);
|
||
}
|
||
long fastNs = System.nanoTime() - t1;
|
||
|
||
assert slowResult == fastResult : "results differ: " + slowResult + " vs " + fastResult;
|
||
double ratio = (double) slowNs / Math.max(fastNs, 1);
|
||
System.out.printf("PASS stress P=%d R=%d C=%d V=%d 100x: slow=%dms fast=%dms ratio=%.1fx%n",
|
||
P, R, C, V, slowNs / 1_000_000, fastNs / 1_000_000, ratio);
|
||
// ops count test already proves O(V) vs O(1) — just verify correctness here
|
||
assert slowResult > 0 || fastResult == 0 : "unexpected mismatch";
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Main
|
||
// -------------------------------------------------------------------------
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("=== Kubernetes0007Algorithm: pod failure policy exit-code scan (kubernetes-0007) ===");
|
||
testCorrectness();
|
||
testOpsCount();
|
||
testPerf_P500_R15_C3_V30();
|
||
testPerf_P2000_R20_C5_V10_stress();
|
||
System.out.println("4/4 PASS");
|
||
}
|
||
}
|