Skip to content

Add support for static domain-to-IP mapping in k8s_dns_server for DNSChaos #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
55 changes: 53 additions & 2 deletions chaos.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const (
ActionError = "error"
// ActionRandom means return random IP for DNS request
ActionRandom = "random"
// ActionChaos means return chaos IP for DNS request
ActionStatic = "static"
)

// PodInfo saves some information for pod
Expand All @@ -39,6 +41,12 @@ type PodInfo struct {
LastUpdateTime time.Time
}

// DomainIP Domain and ip mapping
type DomainIP struct {
Domain string
IP string
}

// IsOverdue ...
func (p *PodInfo) IsOverdue() bool {
// if the pod's IP is not updated greater than 10 seconds, will treate it as overdue
Expand All @@ -55,8 +63,6 @@ func (k Kubernetes) chaosDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
return dns.RcodeServerFailure, fmt.Errorf("dns chaos error")
}

// return random IP

answers := []dns.RR{}
qname := state.Name()

Expand Down Expand Up @@ -166,6 +172,15 @@ func (k Kubernetes) needChaos(podInfo *PodInfo, records []dns.RR, name string) b
if podInfo.Scope == ScopeAll {
return true
}
if podInfo.Action == ActionStatic {
domainMap := k.domainAndIPMap[podInfo.Namespace][podInfo.Name]
if domainMap != nil {
if _, ok := domainMap[name]; ok {
return true
}
}
return false
}

rules := podInfo.Selector.Match(name, "")
if len(rules) == 0 {
Expand All @@ -188,3 +203,39 @@ func (k Kubernetes) getPodFromCluster(namespace, name string) (*api.Pod, error)
}
return pods.Get(context.Background(), name, meta.GetOptions{})
}

func generateDNSRecords(state request.Request, domainAndIpMap map[string]string, r *dns.Msg, w dns.ResponseWriter) (int, error) {
answers := []dns.RR{}
qname := state.Name()
if domainAndIpMap == nil {
return dns.RcodeServerFailure, nil
}
ipStr, ok := domainAndIpMap[qname]
if !ok {
return dns.RcodeServerFailure, fmt.Errorf("domain %s not found", qname)
}
ip := net.ParseIP(ipStr)
switch state.QType() {
case dns.TypeA:
ipv4 := ip.To4()
if ipv4 == nil {
return dns.RcodeServerFailure, fmt.Errorf("not a valid IPv4 address: %s", ipStr)
}
answers = a(qname, 10, []net.IP{ipv4})
log.Debugf("dns.TypeA %v", ipv4)
case dns.TypeAAAA:
ipv6 := ip.To16()
if ip.To4() != nil {
return dns.RcodeServerFailure, fmt.Errorf("not a valid IPv6 address: %s", ipStr)
}
log.Debugf("dns.TypeAAAA %v", ipv6)
answers = aaaa(qname, 10, []net.IP{ipv6})
}
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative = true
m.Answer = answers

w.WriteMsg(m)
return dns.RcodeSuccess, nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
github.com/miekg/dns v1.1.43
github.com/pingcap/tidb-tools v6.3.0+incompatible
github.com/prometheus/client_golang v1.11.0
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
google.golang.org/grpc v1.41.0
k8s.io/api v0.22.2
k8s.io/apimachinery v0.22.2
Expand Down Expand Up @@ -45,6 +44,7 @@ require (
github.com/prometheus/common v0.31.1 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
Expand Down
36 changes: 34 additions & 2 deletions grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ func (k Kubernetes) SetDNSChaos(ctx context.Context, req *pb.SetDNSChaosRequest)

k.Lock()
defer k.Unlock()

k.chaosMap[req.Name] = req

var scope string
Expand All @@ -70,6 +69,15 @@ func (k Kubernetes) SetDNSChaos(ctx context.Context, req *pb.SetDNSChaosRequest)
}
}
}
if req.Action == ActionStatic && req.IpDomainMaps != nil {
for _, domainIPMap := range req.IpDomainMaps {
err := selector.Insert(domainIPMap.Domain, "", true, trieselector.Insert)
if err != nil {
log.Errorf("fail to build selector %v", err)
return nil, err
}
}
}

for _, pod := range req.Pods {
v1Pod, err := k.getPodFromCluster(pod.Namespace, pod.Name)
Expand Down Expand Up @@ -100,8 +108,15 @@ func (k Kubernetes) SetDNSChaos(ctx context.Context, req *pb.SetDNSChaosRequest)

k.podMap[pod.Namespace][pod.Name] = podInfo
k.ipPodMap[v1Pod.Status.PodIP] = podInfo
}
domainIPMap := saveDomainAndIp(req.IpDomainMaps)
if domainIPMap != nil {
if _, ok := k.domainAndIPMap[pod.Namespace]; !ok {
k.domainAndIPMap[pod.Namespace] = make(map[string]map[string]string)
}
k.domainAndIPMap[pod.Namespace][pod.Name] = domainIPMap
}

}
return &pb.DNSChaosResponse{
Result: true,
}, nil
Expand All @@ -125,6 +140,9 @@ func (k Kubernetes) CancelDNSChaos(ctx context.Context, req *pb.CancelDNSChaosRe
delete(k.podMap[pod.Namespace], pod.Name)
delete(k.ipPodMap, podInfo.IP)
}
if _, ok1 := k.domainAndIPMap[pod.Namespace][pod.Name]; ok1 {
delete(k.domainAndIPMap[pod.Namespace], pod.Name)
}
}
}

Expand All @@ -136,6 +154,7 @@ func (k Kubernetes) CancelDNSChaos(ctx context.Context, req *pb.CancelDNSChaosRe
}
for _, namespace := range shouldDeleteNs {
delete(k.podMap, namespace)
delete(k.domainAndIPMap, namespace)
}

delete(k.chaosMap, req.Name)
Expand All @@ -144,3 +163,16 @@ func (k Kubernetes) CancelDNSChaos(ctx context.Context, req *pb.CancelDNSChaosRe
Result: true,
}, nil
}

// save domain and ip
func saveDomainAndIp(domainMapList []*pb.IpDomainMap) map[string]string {
if len(domainMapList) == 0 {
return nil
}
domainIPMap := make(map[string]string)
for _, domainMap := range domainMapList {
key := fmt.Sprintf("%s.", domainMap.Domain)
domainIPMap[key] = domainMap.Ip
}
return domainIPMap
}
18 changes: 15 additions & 3 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package kubernetes

import (
"context"

"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/request"

Expand All @@ -22,10 +21,23 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M

records, extra, zone, err := k.getRecords(ctx, state)
log.Debugf("records: %v, err: %v", records, err)

if k.needChaos(chaosPod, records, state.QName()) {
if k.needChaos(chaosPod, records, state.QName()) && chaosPod.Action != ActionStatic {
return k.chaosDNS(ctx, w, r, state, chaosPod)
}
// Check if chaos testing is needed and the action type is static IP.
if k.needChaos(chaosPod, records, state.QName()) && chaosPod.Action == ActionStatic {
log.Debugf("need chaos, but action is static")
// Get the domain-IP mapping for the specific namespace and pod name.
domainMap := k.domainAndIPMap[chaosPod.Namespace][chaosPod.Name]
// Check if the domain-IP mapping exists.
if domainMap != nil {
// Check if the requested domain exists in the mapping.
if _, ok := domainMap[state.Name()]; ok {
// Generate DNS records using the domain-IP mapping and return the result.
return generateDNSRecords(state, domainMap, r, w)
}
}
}

if k.IsNameError(err) {
if len(zone) == 0 {
Expand Down
5 changes: 4 additions & 1 deletion kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"context"
"errors"
"fmt"
"github.com/chaos-mesh/k8s_dns_chaos/pb"
"math/rand"
"net"
"strings"
"sync"
"time"

"github.com/chaos-mesh/k8s_dns_chaos/pb"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/etcd/msg"
"github.com/coredns/coredns/plugin/kubernetes/object"
Expand Down Expand Up @@ -68,6 +68,8 @@ type Kubernetes struct {
podMap map[string]map[string]*PodInfo

ipPodMap map[string]*PodInfo

domainAndIPMap map[string]map[string]map[string]string
}

// New returns a initialized Kubernetes. It default interfaceAddrFunc to return 127.0.0.1. All other
Expand All @@ -81,6 +83,7 @@ func New(zones []string) *Kubernetes {
k.chaosMap = make(map[string]*pb.SetDNSChaosRequest)
k.podMap = make(map[string]map[string]*PodInfo)
k.ipPodMap = make(map[string]*PodInfo)
k.domainAndIPMap = make(map[string]map[string]map[string]string)
rand.Seed(time.Now().UnixNano())

return k
Expand Down
Loading