Skip to content

Commit 5697a28

Browse files
author
Zeusro
committed
Tue Jan 6 22:30:23 CST 2026
1 parent 082476f commit 5697a28

7 files changed

Lines changed: 138 additions & 40 deletions

File tree

cmd/killer/deployment_killer.go

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package killer
22

33
import (
4-
"context"
5-
"fmt"
6-
74
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
85

96
"github.com/p-program/kube-killer/core"
@@ -60,22 +57,3 @@ func (k *DeploymentKiller) DryRun() *DeploymentKiller {
6057
func (k *DeploymentKiller) Kill() {
6158

6259
}
63-
64-
func (k *DeploymentKiller) kill2(resources []string) {
65-
fmt.Printf("bilibili:")
66-
fmt.Println(resources)
67-
// resourceType := resources[0]
68-
resourceName := resources[1]
69-
// create the clientset
70-
clientset, err := kubernetes.NewForConfig(core.GLOBAL_KUBERNETES_CONFIG)
71-
if err != nil {
72-
panic(err.Error())
73-
}
74-
namespace := ""
75-
err = clientset.CoreV1().Pods(namespace).Delete(context.TODO(), resourceName, metav1.DeleteOptions{})
76-
// clientset.AppsV1().Deployments()
77-
if err != nil {
78-
panic(err.Error())
79-
}
80-
// fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
81-
}

cmd/killer/node_killer.go

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/rs/zerolog/log"
88

99
"github.com/p-program/kube-killer/core"
10+
v1 "k8s.io/api/core/v1"
1011
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1112
"k8s.io/apimachinery/pkg/types"
1213
"k8s.io/apimachinery/pkg/util/strategicpatch"
@@ -26,12 +27,15 @@ func NewNodeKiller(nodeName string) (*NodeKiller, error) {
2627
if err != nil {
2728
return nil, err
2829
}
29-
k := NodeKiller{client: clientset}
30+
k := NodeKiller{
31+
client: clientset,
32+
nodeName: nodeName,
33+
}
3034
var gracePeriodSeconds int64 = 0
3135
k.deleteOption = metav1.DeleteOptions{
3236
GracePeriodSeconds: &gracePeriodSeconds,
3337
}
34-
return &k, err
38+
return &k, nil
3539
}
3640

3741
func (k *NodeKiller) BlackHand() *NodeKiller {
@@ -79,9 +83,71 @@ func (k *NodeKiller) Kill() error {
7983
if err != nil {
8084
return err
8185
}
82-
//# TODO:驱逐除了ds以外所有的pod
83-
//kubectl drain $node --ignore-daemonsets
86+
// Drain all pods from the node except DaemonSet pods
87+
// Similar to: kubectl drain $node --ignore-daemonsets
88+
err = k.drainNodePods(clientset)
89+
if err != nil {
90+
return err
91+
}
8492
}
8593
//kubectl delete $node
8694
return nil
8795
}
96+
97+
// drainNodePods evicts all pods from the node except DaemonSet pods
98+
// Similar to: kubectl drain $node --ignore-daemonsets
99+
func (k *NodeKiller) drainNodePods(clientset *kubernetes.Clientset) error {
100+
log.Info().Msgf("Draining pods from node %s (ignoring DaemonSet pods)", k.nodeName)
101+
102+
// Get all pods on this node
103+
fieldSelector := "spec.nodeName=" + k.nodeName
104+
pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{
105+
FieldSelector: fieldSelector,
106+
})
107+
if err != nil {
108+
return err
109+
}
110+
111+
evictedCount := 0
112+
for _, pod := range pods.Items {
113+
// Check if pod belongs to a DaemonSet
114+
if k.isDaemonSetPod(pod) {
115+
log.Info().Msgf("Skipping DaemonSet pod %s/%s", pod.Namespace, pod.Name)
116+
continue
117+
}
118+
119+
log.Info().Msgf("Evicting pod %s/%s from node %s", pod.Namespace, pod.Name, k.nodeName)
120+
err = clientset.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, k.deleteOption)
121+
if err != nil {
122+
log.Error().Err(err).Msgf("Failed to evict pod %s/%s", pod.Namespace, pod.Name)
123+
continue
124+
}
125+
evictedCount++
126+
}
127+
128+
log.Info().Msgf("Evicted %d pods from node %s", evictedCount, k.nodeName)
129+
return nil
130+
}
131+
132+
// isDaemonSetPod checks if a pod belongs to a DaemonSet by examining OwnerReferences
133+
func (k *NodeKiller) isDaemonSetPod(pod v1.Pod) bool {
134+
for _, ownerRef := range pod.OwnerReferences {
135+
if ownerRef.Kind == "DaemonSet" {
136+
return true
137+
}
138+
// Check if it's owned by a ReplicaSet that belongs to a DaemonSet
139+
// This is a more thorough check, but for simplicity, we'll check direct ownership
140+
if ownerRef.Kind == "ReplicaSet" {
141+
// Get the ReplicaSet to check its owner
142+
rs, err := k.client.AppsV1().ReplicaSets(pod.Namespace).Get(context.TODO(), ownerRef.Name, metav1.GetOptions{})
143+
if err == nil {
144+
for _, rsOwnerRef := range rs.OwnerReferences {
145+
if rsOwnerRef.Kind == "DaemonSet" {
146+
return true
147+
}
148+
}
149+
}
150+
}
151+
}
152+
return false
153+
}

cmd/killer/pod_killer.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,30 @@ func (k *PodKiller) getAllPodsInCurrentNamespace() ([]*v1.Pod, error) {
123123
}
124124

125125
func (k *PodKiller) KillHalfPods() error {
126-
//TODO
126+
log.Warn().Msg("KillHalfPods")
127+
pods, err := k.getPods(nil)
128+
if err != nil {
129+
return err
130+
}
131+
if len(pods.Items) == 0 {
132+
log.Info().Msg("No pods to kill")
133+
return nil
134+
}
135+
// Calculate how many pods to kill (half, rounded down)
136+
podsToKill := len(pods.Items) / 2
137+
if podsToKill == 0 {
138+
podsToKill = 1 // At least kill one pod if there's only one
139+
}
140+
log.Info().Msgf("Killing %d out of %d pods", podsToKill, len(pods.Items))
141+
for i := 0; i < podsToKill; i++ {
142+
pod := pods.Items[i]
143+
podName := pod.Name
144+
log.Warn().Msgf("delete pod %s in namespace %s", podName, pod.Namespace)
145+
err = k.client.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), podName, k.deleteOption)
146+
if err != nil {
147+
log.Error().Err(err)
148+
}
149+
}
127150
return nil
128151
}
129152

cmd/killer/pv_killer.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,31 +92,31 @@ func (k *PVKiller) KillUnusedPVs() error {
9292
if err != nil {
9393
return err
9494
}
95-
95+
9696
// Get all PVCs across all namespaces to check which PVs are in use
9797
// Note: PVs are cluster-scoped, so we need to check all namespaces
9898
pvcList, err := k.client.CoreV1().PersistentVolumeClaims("").List(context.TODO(), metav1.ListOptions{})
9999
if err != nil {
100100
log.Warn().Err(err).Msg("Failed to list PVCs, will only check PV phase")
101101
}
102-
102+
103103
// Build map of PVs that are bound to PVCs
104104
usedPVs := make(map[string]bool)
105105
for _, pvc := range pvcList.Items {
106106
if pvc.Spec.VolumeName != "" {
107107
usedPVs[pvc.Spec.VolumeName] = true
108108
}
109109
}
110-
110+
111111
for _, volume := range volumeList.Items {
112112
volumeName := volume.Name
113113
phase := volume.Status.Phase
114-
114+
115115
// Skip bound volumes that are in use
116116
if phase == v1.VolumeBound && usedPVs[volumeName] {
117117
continue
118118
}
119-
119+
120120
// Delete available, released, or failed volumes
121121
if phase == v1.VolumeAvailable || phase == v1.VolumeReleased || phase == v1.VolumeFailed {
122122
log.Info().Msgf("Deleting unused PV %s (phase: %s)", volumeName, phase)

cmd/killer/statefulset_killer.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package killer
22

33
import (
4+
"context"
5+
46
"github.com/p-program/kube-killer/core"
7+
"github.com/rs/zerolog/log"
58
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
69
"k8s.io/client-go/kubernetes"
710
)
@@ -43,7 +46,24 @@ func (k *StatefulSetKiller) BlackHand() *StatefulSetKiller {
4346
}
4447

4548
func (k *StatefulSetKiller) Kill() error {
46-
// statefulsets,err :=k.client.CoreV1().
47-
//TODO
49+
if k.mafia {
50+
return k.KillAllStatefulSets()
51+
}
52+
return k.KillAllStatefulSets()
53+
}
54+
55+
func (k *StatefulSetKiller) KillAllStatefulSets() error {
56+
log.Warn().Msg("KillAllStatefulSets")
57+
statefulSets, err := k.client.AppsV1().StatefulSets(k.namespace).List(context.TODO(), metav1.ListOptions{})
58+
if err != nil {
59+
return err
60+
}
61+
for _, sts := range statefulSets.Items {
62+
log.Warn().Msgf("deleting statefulset %s in namespace %s", sts.Name, k.namespace)
63+
err = k.client.AppsV1().StatefulSets(k.namespace).Delete(context.TODO(), sts.Name, k.deleteOption)
64+
if err != nil {
65+
log.Error().Err(err)
66+
}
67+
}
4868
return nil
4969
}

cmd/killer/zeusro.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import (
1111
// Coin live or dead ?
1212
func (z *Zeusro) Coin() bool {
1313
//prevent same result
14-
rand.Seed(time.Now().UnixNano())
15-
v := rand.Intn(2)
14+
// Use rand.New() instead of deprecated rand.Seed() (Go 1.20+)
15+
r := rand.New(rand.NewSource(time.Now().UnixNano()))
16+
v := r.Intn(2)
1617
return v == 1
1718
}
1819

@@ -78,9 +79,17 @@ func (z *Zeusro) callSheldon() {
7879
}
7980

8081
func (z *Zeusro) callMyWife() {
81-
//TODO
82+
fmt.Println("Penny: What? What happened?")
83+
time.Sleep(time.Second)
84+
fmt.Println("Penny: Oh my god, are you okay?")
85+
time.Sleep(time.Second)
86+
fmt.Println("Penny: I'm coming over right now!")
8287
}
8388

8489
func (z *Zeusro) callThanos() {
85-
//TODO
90+
fmt.Println("Thanos: I am inevitable.")
91+
time.Sleep(time.Second * 2)
92+
fmt.Println("Thanos: *snaps fingers*")
93+
time.Sleep(time.Second)
94+
fmt.Println("Thanos: The universe will be balanced.")
8695
}

cmd/killer/zeusro_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import (
44
"fmt"
55
"sync"
66
"testing"
7+
8+
"github.com/p-program/kube-killer/config"
79
)
810

911
func prepareZeusro() *Zeusro {
10-
// TODO
11-
return nil
12+
projectConfig := config.NewProjectConfig()
13+
return NewZeusro(projectConfig, false)
1214
}
1315

1416
func TestCoin(t *testing.T) {

0 commit comments

Comments
 (0)