Skip to content

Commit 2f6ee29

Browse files
refine code && support scope and mode for chaos (#6)
Signed-off-by: xiang <[email protected]>
1 parent ef5f441 commit 2f6ee29

File tree

6 files changed

+227
-90
lines changed

6 files changed

+227
-90
lines changed

chaos.go

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,57 @@ import (
44
"context"
55
"math/rand"
66
"net"
7+
"time"
78

9+
"github.com/coredns/coredns/plugin"
810
"github.com/coredns/coredns/request"
911
"github.com/miekg/dns"
1012
api "k8s.io/api/core/v1"
1113
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
1214
)
1315

14-
func (k Kubernetes) chaosDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg, state request.Request) (int, error) {
16+
const (
17+
// ScopeInner means chaos only works on the inner host in Kubernetes cluster
18+
ScopeInner = "INNER"
19+
// ScopeOuter means chaos only works on the outer host of Kubernetes cluster
20+
ScopeOuter = "OUTER"
21+
// ScopeAll means chaos works on all host
22+
ScopeAll = "ALL"
23+
24+
// ModeError means return error for DNS request
25+
ModeError = "ERROR"
26+
// ModeRandom means return random IP for DNS request
27+
ModeRandom = "RANDOM"
28+
)
29+
30+
// PodInfo saves some information for pod
31+
type PodInfo struct {
32+
Namespace string
33+
Name string
34+
Mode string
35+
Scope string
36+
IP string
37+
LastUpdateTime time.Time
38+
}
39+
40+
// IsOverdue ...
41+
func (p *PodInfo) IsOverdue() bool {
42+
// if the pod's IP is not updated greater than 10 seconds, will treate it as overdue
43+
// and need to update it
44+
if time.Since(p.LastUpdateTime) > time.Duration(time.Second*10) {
45+
return true
46+
}
47+
48+
return false
49+
}
50+
51+
func (k Kubernetes) chaosDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg, state request.Request, podInfo *PodInfo) (int, error) {
52+
if podInfo.Mode == ModeError {
53+
return dns.RcodeServerFailure, nil
54+
}
55+
56+
// return random IP
57+
1558
answers := []dns.RR{}
1659
qname := state.Name()
1760

@@ -22,6 +65,7 @@ func (k Kubernetes) chaosDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
2265
log.Infof("dns.TypeA %v", ips)
2366
answers = a(qname, 10, ips)
2467
case dns.TypeAAAA:
68+
// TODO: return random IP
2569
ips := []net.IP{net.IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}}
2670
log.Infof("dns.TypeAAAA %v", ips)
2771
answers = aaaa(qname, 10, ips)
@@ -77,35 +121,68 @@ func aaaa(zone string, ttl uint32, ips []net.IP) []dns.RR {
77121
return answers
78122
}
79123

80-
func (k Kubernetes) getChaosMode(pod *api.Pod) string {
124+
func (k Kubernetes) getChaosPod(ip string) (*PodInfo, error) {
81125
k.RLock()
82-
defer k.RUnlock()
83126

84-
if pod == nil {
85-
return ""
127+
podInfo := k.ipPodMap[ip]
128+
if podInfo == nil {
129+
k.RUnlock()
130+
return nil, nil
86131
}
87132

88-
if _, ok := k.podChaosMap[pod.Namespace]; ok {
89-
return k.podChaosMap[pod.Namespace][pod.Name]
133+
if podInfo.IsOverdue() {
134+
k.RUnlock()
135+
136+
v1Pod, err := k.getPodFromCluster(podInfo.Namespace, podInfo.Name)
137+
if err != nil {
138+
return nil, err
139+
}
140+
141+
if v1Pod.Status.PodIP != podInfo.IP {
142+
k.Lock()
143+
podInfo.IP = v1Pod.Status.PodIP
144+
podInfo.LastUpdateTime = time.Now()
145+
146+
delete(k.ipPodMap, podInfo.IP)
147+
k.ipPodMap[v1Pod.Status.PodIP] = podInfo
148+
k.Unlock()
149+
}
150+
151+
return podInfo, nil
90152
}
91153

92-
return ""
154+
k.RUnlock()
155+
return podInfo, nil
93156
}
94157

95-
func (k Kubernetes) getChaosPod() ([]api.Pod, error) {
96-
k.RLock()
97-
defer k.RUnlock()
158+
// needChaos judges weather should do chaos for the request
159+
func (k Kubernetes) needChaos(podInfo *PodInfo, state request.Request) bool {
160+
if podInfo == nil {
161+
return false
162+
}
98163

99-
pods := make([]api.Pod, 0, 10)
100-
for namespace := range k.podChaosMap {
101-
podList, err := k.Client.Pods(namespace).List(context.Background(), meta.ListOptions{})
102-
if err != nil {
103-
return nil, err
164+
if podInfo.Scope == ScopeAll {
165+
return true
166+
}
167+
168+
qname := state.QName()
169+
zone := plugin.Zones(k.Zones).Matches(qname)
170+
171+
if zone == "" {
172+
// is outer host
173+
if podInfo.Scope == ScopeOuter {
174+
return true
104175
}
105-
for _, pod := range podList.Items {
106-
pods = append(pods, pod)
176+
} else {
177+
// is inner host
178+
if podInfo.Scope == ScopeInner {
179+
return true
107180
}
108181
}
109182

110-
return pods, nil
183+
return false
184+
}
185+
186+
func (k Kubernetes) getPodFromCluster(namespace, name string) (*api.Pod, error) {
187+
return k.Client.Pods(namespace).Get(context.Background(), name, meta.GetOptions{})
111188
}

grpc_server.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package kubernetes
33
import (
44
"context"
55
"net"
6+
"time"
67

78
"github.com/chaos-mesh/k8s_dns_chaos/pb"
89
"google.golang.org/grpc"
@@ -30,15 +31,39 @@ func (k Kubernetes) CreateGRPCServer(port string) error {
3031
// SetDNSChaos ...
3132
func (k Kubernetes) SetDNSChaos(ctx context.Context, req *pb.SetDNSChaosRequest) (*pb.DNSChaosResponse, error) {
3233
log.Infof("receive SetDNSChaos request %v", req)
34+
3335
k.Lock()
3436
defer k.Unlock()
3537

3638
k.chaosMap[req.Name] = req
39+
3740
for _, pod := range req.Pods {
38-
if _, ok := k.podChaosMap[pod.Namespace]; !ok {
39-
k.podChaosMap[pod.Namespace] = make(map[string]string)
41+
v1Pod, err := k.getPodFromCluster(pod.Namespace, pod.Name)
42+
if err != nil {
43+
return nil, err
4044
}
41-
k.podChaosMap[pod.Namespace][pod.Name] = req.Mode
45+
46+
if _, ok := k.podMap[pod.Namespace]; !ok {
47+
k.podMap[pod.Namespace] = make(map[string]*PodInfo)
48+
}
49+
50+
if oldPod, ok := k.podMap[pod.Namespace][pod.Name]; ok {
51+
// Pod's IP maybe changed, so delete the old pod info
52+
delete(k.podMap[pod.Namespace], pod.Name)
53+
delete(k.ipPodMap, oldPod.IP)
54+
}
55+
56+
podInfo := &PodInfo{
57+
Namespace: pod.Namespace,
58+
Name: pod.Name,
59+
Mode: req.Mode,
60+
Scope: req.Scope,
61+
IP: v1Pod.Status.PodIP,
62+
LastUpdateTime: time.Now(),
63+
}
64+
65+
k.podMap[pod.Namespace][pod.Name] = podInfo
66+
k.ipPodMap[v1Pod.Status.PodIP] = podInfo
4267
}
4368

4469
return &pb.DNSChaosResponse{
@@ -52,19 +77,22 @@ func (k Kubernetes) CancelDNSChaos(ctx context.Context, req *pb.CancelDNSChaosRe
5277
k.Lock()
5378
defer k.Unlock()
5479
for _, pod := range k.chaosMap[req.Name].Pods {
55-
if _, ok := k.podChaosMap[pod.Namespace]; ok {
56-
delete(k.podChaosMap[pod.Namespace], pod.Name)
80+
if _, ok := k.podMap[pod.Namespace]; ok {
81+
if podInfo, ok := k.podMap[pod.Namespace][pod.Name]; ok {
82+
delete(k.podMap[pod.Namespace], pod.Name)
83+
delete(k.ipPodMap, podInfo.IP)
84+
}
5785
}
5886
}
5987

6088
shouldDeleteNs := make([]string, 0, 1)
61-
for namespace, pods := range k.podChaosMap {
89+
for namespace, pods := range k.podMap {
6290
if len(pods) == 0 {
6391
shouldDeleteNs = append(shouldDeleteNs, namespace)
6492
}
6593
}
6694
for _, namespace := range shouldDeleteNs {
67-
delete(k.podChaosMap, namespace)
95+
delete(k.podMap, namespace)
6896
}
6997

7098
delete(k.chaosMap, req.Name)

handler.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55

66
"github.com/coredns/coredns/plugin"
77
"github.com/coredns/coredns/request"
8-
api "k8s.io/api/core/v1"
98

109
"github.com/miekg/dns"
1110
)
@@ -16,21 +15,12 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
1615
sourceIP := state.IP()
1716
log.Infof("k8s ServeDNS, source IP: %s", sourceIP)
1817

19-
var sourcePod *api.Pod
20-
21-
pods, err := k.getChaosPod()
18+
chaosPod, err := k.getChaosPod(sourceIP)
2219
if err != nil {
23-
log.Errorf("list pods, error %v", err)
24-
}
25-
for _, pod := range pods {
26-
log.Infof("list pod name: %s, ip: %s", pod.Name, pod.Status.PodIP)
27-
if pod.Status.PodIP == sourceIP {
28-
sourcePod = &pod
29-
}
20+
log.Infof("fail to get pod information from cluster, IP: %s, error: %v", sourceIP, err)
3021
}
31-
mode := k.getChaosMode(sourcePod)
32-
if len(mode) != 0 {
33-
return k.chaosDNS(ctx, w, r, state)
22+
if k.needChaos(chaosPod, state) {
23+
return k.chaosDNS(ctx, w, r, state, chaosPod)
3424
}
3525

3626
qname := state.QName()

kubernetes.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ type Kubernetes struct {
5757
localIPs []net.IP
5858
autoPathSearch []string // Local search path from /etc/resolv.conf. Needed for autopath.
5959
TransferTo []string
60-
Client typev1.CoreV1Interface //client.Client
60+
Client typev1.CoreV1Interface
6161

6262
sync.RWMutex
6363
chaosMap map[string]*pb.SetDNSChaosRequest
64-
// namespace -> pod_name -> chaos_mode
65-
podChaosMap map[string]map[string]string
64+
// namespace -> pod_name -> pod info
65+
podMap map[string]map[string]*PodInfo
66+
67+
ipPodMap map[string]*PodInfo
6668
}
6769

6870
// New returns a initialized Kubernetes. It default interfaceAddrFunc to return 127.0.0.1. All other
@@ -74,7 +76,8 @@ func New(zones []string) *Kubernetes {
7476
k.podMode = podModeDisabled
7577
k.ttl = defaultTTL
7678
k.chaosMap = make(map[string]*pb.SetDNSChaosRequest)
77-
k.podChaosMap = make(map[string]map[string]string)
79+
k.podMap = make(map[string]map[string]*PodInfo)
80+
k.ipPodMap = make(map[string]*PodInfo)
7881
rand.Seed(time.Now().UnixNano())
7982

8083
return k

0 commit comments

Comments
 (0)