|
1 | 1 | package helpers |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
4 | 5 | "fmt" |
5 | 6 | "os/exec" |
6 | 7 | "strings" |
@@ -75,43 +76,191 @@ func EnsureNamespaceExists(kubeconfig, namespace string) error { |
75 | 76 | return nil |
76 | 77 | } |
77 | 78 |
|
78 | | -// DeletePod deletes a pod in the specified namespace |
| 79 | +// DeletePod deletes a pod in the specified namespace and waits for it to be fully removed |
79 | 80 | func DeletePod(kubeconfig, namespace, podName string) error { |
80 | | - cmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "delete", "pod", podName, "-n", namespace, "--ignore-not-found=true") |
| 81 | + fmt.Printf("Deleting pod %s in namespace %s...\n", podName, namespace) |
| 82 | + |
| 83 | + // Initiate pod deletion with context timeout |
| 84 | + ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) |
| 85 | + defer cancel() |
| 86 | + |
| 87 | + cmd := exec.CommandContext(ctx, "kubectl", "--kubeconfig", kubeconfig, "delete", "pod", podName, "-n", namespace, "--ignore-not-found=true") |
81 | 88 | out, err := cmd.CombinedOutput() |
82 | 89 | if err != nil { |
83 | | - return fmt.Errorf("failed to delete pod %s in namespace %s: %s\n%s", podName, namespace, err, string(out)) |
| 90 | + if ctx.Err() == context.DeadlineExceeded { |
| 91 | + fmt.Printf("kubectl delete pod command timed out after 90s, attempting force delete...\n") |
| 92 | + } else { |
| 93 | + return fmt.Errorf("failed to delete pod %s in namespace %s: %s\n%s", podName, namespace, err, string(out)) |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + // Wait for pod to be completely gone (critical for IP release) |
| 98 | + fmt.Printf("Waiting for pod %s to be fully removed...\n", podName) |
| 99 | + for attempt := 1; attempt <= 30; attempt++ { |
| 100 | + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) |
| 101 | + checkCmd := exec.CommandContext(ctx, "kubectl", "--kubeconfig", kubeconfig, "get", "pod", podName, "-n", namespace, "--ignore-not-found=true", "-o", "name") |
| 102 | + checkOut, _ := checkCmd.CombinedOutput() |
| 103 | + cancel() |
| 104 | + |
| 105 | + if strings.TrimSpace(string(checkOut)) == "" { |
| 106 | + fmt.Printf("Pod %s fully removed after %d seconds\n", podName, attempt*2) |
| 107 | + // Extra wait to ensure IP reservation is released in DNC |
| 108 | + time.Sleep(5 * time.Second) |
| 109 | + return nil |
| 110 | + } |
| 111 | + |
| 112 | + if attempt%5 == 0 { |
| 113 | + fmt.Printf("Pod %s still terminating (attempt %d/30)...\n", podName, attempt) |
| 114 | + } |
| 115 | + time.Sleep(2 * time.Second) |
| 116 | + } |
| 117 | + |
| 118 | + // If pod still exists after 60 seconds, force delete |
| 119 | + fmt.Printf("Pod %s still exists after 60s, attempting force delete...\n", podName) |
| 120 | + ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second) |
| 121 | + defer cancel() |
| 122 | + |
| 123 | + forceCmd := exec.CommandContext(ctx, "kubectl", "--kubeconfig", kubeconfig, "delete", "pod", podName, "-n", namespace, "--grace-period=0", "--force", "--ignore-not-found=true") |
| 124 | + forceOut, forceErr := forceCmd.CombinedOutput() |
| 125 | + if forceErr != nil { |
| 126 | + fmt.Printf("Warning: Force delete failed: %s\n%s\n", forceErr, string(forceOut)) |
84 | 127 | } |
| 128 | + |
| 129 | + // Wait a bit more for force delete to complete |
| 130 | + time.Sleep(10 * time.Second) |
| 131 | + fmt.Printf("Pod %s deletion completed (may have required force)\n", podName) |
85 | 132 | return nil |
86 | 133 | } |
87 | 134 |
|
88 | | -// DeletePodNetworkInstance deletes a PodNetworkInstance |
| 135 | +// DeletePodNetworkInstance deletes a PodNetworkInstance and waits for it to be removed |
89 | 136 | func DeletePodNetworkInstance(kubeconfig, namespace, pniName string) error { |
| 137 | + fmt.Printf("Deleting PodNetworkInstance %s in namespace %s...\n", pniName, namespace) |
| 138 | + |
| 139 | + // Initiate PNI deletion |
90 | 140 | cmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "delete", "podnetworkinstance", pniName, "-n", namespace, "--ignore-not-found=true") |
91 | 141 | out, err := cmd.CombinedOutput() |
92 | 142 | if err != nil { |
93 | 143 | return fmt.Errorf("failed to delete PodNetworkInstance %s: %s\n%s", pniName, err, string(out)) |
94 | 144 | } |
| 145 | + |
| 146 | + // Wait for PNI to be completely gone (it may take time for DNC to release reservations) |
| 147 | + fmt.Printf("Waiting for PodNetworkInstance %s to be fully removed...\n", pniName) |
| 148 | + for attempt := 1; attempt <= 60; attempt++ { |
| 149 | + checkCmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "get", "podnetworkinstance", pniName, "-n", namespace, "--ignore-not-found=true", "-o", "name") |
| 150 | + checkOut, _ := checkCmd.CombinedOutput() |
| 151 | + |
| 152 | + if strings.TrimSpace(string(checkOut)) == "" { |
| 153 | + fmt.Printf("PodNetworkInstance %s fully removed after %d seconds\n", pniName, attempt*2) |
| 154 | + return nil |
| 155 | + } |
| 156 | + |
| 157 | + if attempt%10 == 0 { |
| 158 | + // Check for ReservationInUse errors |
| 159 | + descCmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "describe", "podnetworkinstance", pniName, "-n", namespace) |
| 160 | + descOut, _ := descCmd.CombinedOutput() |
| 161 | + descStr := string(descOut) |
| 162 | + |
| 163 | + if strings.Contains(descStr, "ReservationInUse") { |
| 164 | + fmt.Printf("PNI %s still has active reservations (attempt %d/60). Waiting for DNC to release...\n", pniName, attempt) |
| 165 | + } else { |
| 166 | + fmt.Printf("PNI %s still terminating (attempt %d/60)...\n", pniName, attempt) |
| 167 | + } |
| 168 | + } |
| 169 | + time.Sleep(2 * time.Second) |
| 170 | + } |
| 171 | + |
| 172 | + // If PNI still exists after 120 seconds, try to remove finalizers |
| 173 | + fmt.Printf("PNI %s still exists after 120s, attempting to remove finalizers...\n", pniName) |
| 174 | + patchCmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "patch", "podnetworkinstance", pniName, "-n", namespace, "-p", `{"metadata":{"finalizers":[]}}`, "--type=merge") |
| 175 | + patchOut, patchErr := patchCmd.CombinedOutput() |
| 176 | + if patchErr != nil { |
| 177 | + fmt.Printf("Warning: Failed to remove finalizers: %s\n%s\n", patchErr, string(patchOut)) |
| 178 | + } else { |
| 179 | + fmt.Printf("Finalizers removed, waiting for deletion...\n") |
| 180 | + time.Sleep(5 * time.Second) |
| 181 | + } |
| 182 | + |
| 183 | + fmt.Printf("PodNetworkInstance %s deletion completed\n", pniName) |
95 | 184 | return nil |
96 | 185 | } |
97 | 186 |
|
98 | | -// DeletePodNetwork deletes a PodNetwork |
| 187 | +// DeletePodNetwork deletes a PodNetwork and waits for it to be removed |
99 | 188 | func DeletePodNetwork(kubeconfig, pnName string) error { |
| 189 | + fmt.Printf("Deleting PodNetwork %s...\n", pnName) |
| 190 | + |
100 | 191 | cmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "delete", "podnetwork", pnName, "--ignore-not-found=true") |
101 | 192 | out, err := cmd.CombinedOutput() |
102 | 193 | if err != nil { |
103 | 194 | return fmt.Errorf("failed to delete PodNetwork %s: %s\n%s", pnName, err, string(out)) |
104 | 195 | } |
| 196 | + |
| 197 | + // Wait for PN to be completely gone |
| 198 | + fmt.Printf("Waiting for PodNetwork %s to be fully removed...\n", pnName) |
| 199 | + for attempt := 1; attempt <= 30; attempt++ { |
| 200 | + checkCmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "get", "podnetwork", pnName, "--ignore-not-found=true", "-o", "name") |
| 201 | + checkOut, _ := checkCmd.CombinedOutput() |
| 202 | + |
| 203 | + if strings.TrimSpace(string(checkOut)) == "" { |
| 204 | + fmt.Printf("PodNetwork %s fully removed after %d seconds\n", pnName, attempt*2) |
| 205 | + return nil |
| 206 | + } |
| 207 | + |
| 208 | + if attempt%10 == 0 { |
| 209 | + fmt.Printf("PodNetwork %s still terminating (attempt %d/30)...\n", pnName, attempt) |
| 210 | + } |
| 211 | + time.Sleep(2 * time.Second) |
| 212 | + } |
| 213 | + |
| 214 | + // Try to remove finalizers if still stuck |
| 215 | + fmt.Printf("PodNetwork %s still exists, attempting to remove finalizers...\n", pnName) |
| 216 | + patchCmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "patch", "podnetwork", pnName, "-p", `{"metadata":{"finalizers":[]}}`, "--type=merge") |
| 217 | + patchOut, patchErr := patchCmd.CombinedOutput() |
| 218 | + if patchErr != nil { |
| 219 | + fmt.Printf("Warning: Failed to remove finalizers: %s\n%s\n", patchErr, string(patchOut)) |
| 220 | + } |
| 221 | + |
| 222 | + time.Sleep(5 * time.Second) |
| 223 | + fmt.Printf("PodNetwork %s deletion completed\n", pnName) |
105 | 224 | return nil |
106 | 225 | } |
107 | 226 |
|
108 | | -// DeleteNamespace deletes a namespace |
| 227 | +// DeleteNamespace deletes a namespace and waits for it to be removed |
109 | 228 | func DeleteNamespace(kubeconfig, namespace string) error { |
| 229 | + fmt.Printf("Deleting namespace %s...\n", namespace) |
| 230 | + |
110 | 231 | cmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "delete", "namespace", namespace, "--ignore-not-found=true") |
111 | 232 | out, err := cmd.CombinedOutput() |
112 | 233 | if err != nil { |
113 | 234 | return fmt.Errorf("failed to delete namespace %s: %s\n%s", namespace, err, string(out)) |
114 | 235 | } |
| 236 | + |
| 237 | + // Wait for namespace to be completely gone |
| 238 | + fmt.Printf("Waiting for namespace %s to be fully removed...\n", namespace) |
| 239 | + for attempt := 1; attempt <= 60; attempt++ { |
| 240 | + checkCmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "get", "namespace", namespace, "--ignore-not-found=true", "-o", "name") |
| 241 | + checkOut, _ := checkCmd.CombinedOutput() |
| 242 | + |
| 243 | + if strings.TrimSpace(string(checkOut)) == "" { |
| 244 | + fmt.Printf("Namespace %s fully removed after %d seconds\n", namespace, attempt*2) |
| 245 | + return nil |
| 246 | + } |
| 247 | + |
| 248 | + if attempt%15 == 0 { |
| 249 | + fmt.Printf("Namespace %s still terminating (attempt %d/60)...\n", namespace, attempt) |
| 250 | + } |
| 251 | + time.Sleep(2 * time.Second) |
| 252 | + } |
| 253 | + |
| 254 | + // Try to remove finalizers if still stuck |
| 255 | + fmt.Printf("Namespace %s still exists, attempting to remove finalizers...\n", namespace) |
| 256 | + patchCmd := exec.Command("kubectl", "--kubeconfig", kubeconfig, "patch", "namespace", namespace, "-p", `{"metadata":{"finalizers":[]}}`, "--type=merge") |
| 257 | + patchOut, patchErr := patchCmd.CombinedOutput() |
| 258 | + if patchErr != nil { |
| 259 | + fmt.Printf("Warning: Failed to remove finalizers: %s\n%s\n", patchErr, string(patchOut)) |
| 260 | + } |
| 261 | + |
| 262 | + time.Sleep(5 * time.Second) |
| 263 | + fmt.Printf("Namespace %s deletion completed\n", namespace) |
115 | 264 | return nil |
116 | 265 | } |
117 | 266 |
|
|
0 commit comments