diff --git a/chaoslib/litmus/network-chaos/lib/network-chaos.go b/chaoslib/litmus/network-chaos/lib/network-chaos.go index e7b935bd4..2f6d405fe 100644 --- a/chaoslib/litmus/network-chaos/lib/network-chaos.go +++ b/chaoslib/litmus/network-chaos/lib/network-chaos.go @@ -2,6 +2,8 @@ package lib import ( "context" + "fmt" + k8serrors "k8s.io/apimachinery/pkg/api/errors" "net" "strconv" "strings" @@ -19,6 +21,8 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +var serviceMesh = []string{"istio", "envoy"} + //PrepareAndInjectChaos contains the prepration & injection steps func PrepareAndInjectChaos(experimentsDetails *experimentTypes.ExperimentDetails, clients clients.ClientSets, resultDetails *types.ResultDetails, eventsDetails *types.EventDetails, chaosDetails *types.ChaosDetails, args string) error { @@ -103,11 +107,6 @@ func PrepareAndInjectChaos(experimentsDetails *experimentTypes.ExperimentDetails } } - experimentsDetails.DestinationIPs, err = GetTargetIps(experimentsDetails.DestinationIPs, experimentsDetails.DestinationHosts) - if err != nil { - return err - } - if experimentsDetails.EngineName != "" { if err := common.SetHelperData(chaosDetails, experimentsDetails.SetHelperData, clients); err != nil { return err @@ -146,6 +145,11 @@ func injectChaosInSerialMode(experimentsDetails *experimentTypes.ExperimentDetai // creating the helper pod to perform network chaos for _, pod := range targetPodList.Items { + experimentsDetails.DestinationIPs, err = GetTargetIps(experimentsDetails.DestinationIPs, experimentsDetails.DestinationHosts, clients, isServiceMeshEnabledForPod(pod)) + if err != nil { + return err + } + //Get the target container name of the application pod if !experimentsDetails.IsTargetContainerProvided { experimentsDetails.TargetContainer, err = common.GetTargetContainer(experimentsDetails.AppNS, pod.Name, clients) @@ -207,6 +211,11 @@ func injectChaosInParallelMode(experimentsDetails *experimentTypes.ExperimentDet // creating the helper pod to perform network chaos for _, pod := range targetPodList.Items { + experimentsDetails.DestinationIPs, err = GetTargetIps(experimentsDetails.DestinationIPs, experimentsDetails.DestinationHosts, clients, isServiceMeshEnabledForPod(pod)) + if err != nil { + return err + } + //Get the target container name of the application pod //It checks the empty target container for the first iteration only if !experimentsDetails.IsTargetContainerProvided { @@ -350,9 +359,9 @@ func getPodEnv(experimentsDetails *experimentTypes.ExperimentDetails, podName, a // GetTargetIps return the comma separated target ips // It fetch the ips from the target ips (if defined by users) // it append the ips from the host, if target host is provided -func GetTargetIps(targetIPs, targetHosts string) (string, error) { +func GetTargetIps(targetIPs, targetHosts string, clients clients.ClientSets, serviceMesh bool) (string, error) { - ipsFromHost, err := getIpsForTargetHosts(targetHosts) + ipsFromHost, err := getIpsForTargetHosts(targetHosts, clients, serviceMesh) if err != nil { return "", err } @@ -364,8 +373,36 @@ func GetTargetIps(targetIPs, targetHosts string) (string, error) { return targetIPs, nil } +// it derive the pod ips from the kubernetes service +func getPodIPFromService(host string, clients clients.ClientSets) ([]string, error) { + var ips []string + svcFields := strings.Split(host, ".") + if len(svcFields) != 5 { + return ips, fmt.Errorf("provide the valid FQDN for service in '..svc.cluster.local format, host: %v", host) + } + svcName, svcNs := svcFields[0], svcFields[1] + svc, err := clients.KubeClient.CoreV1().Services(svcNs).Get(context.Background(), svcName, v1.GetOptions{}) + if err != nil { + if k8serrors.IsForbidden(err) { + log.Warnf("forbidden - failed to get %v service in %v name, err: %v", svcName, svcNs, err) + return ips, nil + } + return ips, err + } + for k, v := range svc.Spec.Selector { + pods, err := clients.KubeClient.CoreV1().Pods(svcNs).List(context.Background(), v1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", k, v)}) + if err != nil { + return ips, err + } + for _, p := range pods.Items { + ips = append(ips, p.Status.PodIP) + } + } + return ips, nil +} + // getIpsForTargetHosts resolves IP addresses for comma-separated list of target hosts and returns comma-separated ips -func getIpsForTargetHosts(targetHosts string) (string, error) { +func getIpsForTargetHosts(targetHosts string, clients clients.ClientSets, serviceMesh bool) (string, error) { if targetHosts == "" { return "", nil } @@ -373,6 +410,14 @@ func getIpsForTargetHosts(targetHosts string) (string, error) { finalHosts := "" var commaSeparatedIPs []string for i := range hosts { + if strings.Contains(hosts[i], "svc.cluster.local") && serviceMesh { + ips, err := getPodIPFromService(hosts[i], clients) + if err != nil { + return "", err + } + commaSeparatedIPs = append(commaSeparatedIPs, ips...) + continue + } ips, err := net.LookupIP(hosts[i]) if err != nil { log.Warnf("Unknown host: {%v}, it won't be included in the scope of chaos", hosts[i]) @@ -404,3 +449,13 @@ func SetChaosTunables(experimentsDetails *experimentTypes.ExperimentDetails) { experimentsDetails.PodsAffectedPerc = common.ValidateRange(experimentsDetails.PodsAffectedPerc) experimentsDetails.Sequence = common.GetRandomSequence(experimentsDetails.Sequence) } + +// It checks if pod contains service mesh sidecar +func isServiceMeshEnabledForPod(pod apiv1.Pod) bool { + for _, c := range pod.Spec.Containers { + if common.StringExistsInSlice(c.Name, serviceMesh) { + return true + } + } + return false +} diff --git a/chaoslib/litmus/pod-network-partition/lib/network-policy.go b/chaoslib/litmus/pod-network-partition/lib/network-policy.go index 6638e0a6b..f8d5c7ff3 100644 --- a/chaoslib/litmus/pod-network-partition/lib/network-policy.go +++ b/chaoslib/litmus/pod-network-partition/lib/network-policy.go @@ -1,6 +1,7 @@ package lib import ( + "github.com/litmuschaos/litmus-go/pkg/clients" "strings" network_chaos "github.com/litmuschaos/litmus-go/chaoslib/litmus/network-chaos/lib" @@ -179,7 +180,7 @@ func getPort(port int32, protocol corev1.Protocol) networkv1.NetworkPolicyPort { // for which traffic should be blocked func (np *NetworkPolicy) setExceptIPs(experimentsDetails *experimentTypes.ExperimentDetails) error { // get all the target ips - destinationIPs, err := network_chaos.GetTargetIps(experimentsDetails.DestinationIPs, experimentsDetails.DestinationHosts) + destinationIPs, err := network_chaos.GetTargetIps(experimentsDetails.DestinationIPs, experimentsDetails.DestinationHosts, clients.ClientSets{}, false) if err != nil { return err } diff --git a/chaoslib/pumba/network-chaos/lib/network-chaos.go b/chaoslib/pumba/network-chaos/lib/network-chaos.go index 6b61bb254..388228b50 100644 --- a/chaoslib/pumba/network-chaos/lib/network-chaos.go +++ b/chaoslib/pumba/network-chaos/lib/network-chaos.go @@ -289,7 +289,7 @@ func createHelperPod(experimentsDetails *experimentTypes.ExperimentDetails, clie // AddTargetIpsArgs inserts a comma-separated list of targetIPs (if provided by the user) into the pumba command/args func AddTargetIpsArgs(targetIPs, targetHosts string, args []string) ([]string, error) { - targetIPs, err := network_chaos.GetTargetIps(targetIPs, targetHosts) + targetIPs, err := network_chaos.GetTargetIps(targetIPs, targetHosts, clients.ClientSets{}, false) if err != nil { return nil, err } diff --git a/pkg/utils/common/common.go b/pkg/utils/common/common.go index 8d8991ed1..e6ad34dd2 100644 --- a/pkg/utils/common/common.go +++ b/pkg/utils/common/common.go @@ -212,3 +212,13 @@ func getRandomValue(a, b int) int { rand.Seed(time.Now().Unix()) return (a + rand.Intn(b-a+1)) } + +// StringExistsInSlice checks the existence of element in slice +func StringExistsInSlice(val string, slice []string) bool { + for _, v := range slice { + if strings.Contains(val, v) { + return true + } + } + return false +}