Repository: pingcap/chaos-mesh
Pull request #117 (2020-01-09 12:58:16)

selector: ignore the phase of the pod when to check if it meets the selector
Signed-off-by: cwen0 <cwenyin0@gmail.com> 
In this pr, we add the namespace in ConfigMaps and ignore the phase of the pod when to check if it meets the selector


Patch:
diff --git a/api/v1alpha1/common_types.go b/api/v1alpha1/common_types.go
index 974bde2b..90ec24c1 100644
--- a/api/v1alpha1/common_types.go
+++ b/api/v1alpha1/common_types.go
@@ -54,6 +54,10 @@ type SelectorSpec struct {
 	// A selector based on annotations.
 	// +optional
 	AnnotationSelectors map[string]string `json:"annotationSelectors,omitempty"`
+
+	// PodPhaseSelector is a set of condition of a pod at the current time.
+	// supported value: Pending / Running / Succeeded / Failed / Unknown
+	PodPhaseSelectors []string `json:"phaseSelector,omitempty"`
 }
 
 // SchedulerSpec defines information about schedule of the chaos experiment.
diff --git a/examples/chaosfs-configmap/etcd-configmap.yaml b/examples/chaosfs-configmap/etcd-configmap.yaml
index ee3303d8..7d7bf6a4 100644
--- a/examples/chaosfs-configmap/etcd-configmap.yaml
+++ b/examples/chaosfs-configmap/etcd-configmap.yaml
@@ -3,6 +3,7 @@ apiVersion: v1
 kind: ConfigMap
 metadata:
   name: chaosfs-etcd
+  namespace: chaos-testing
   labels:
     app.kubernetes.io/component: webhook
 data:
diff --git a/examples/chaosfs-configmap/pd-configmap.yaml b/examples/chaosfs-configmap/pd-configmap.yaml
index 4292bfb2..3312a02f 100644
--- a/examples/chaosfs-configmap/pd-configmap.yaml
+++ b/examples/chaosfs-configmap/pd-configmap.yaml
@@ -3,6 +3,7 @@ apiVersion: v1
 kind: ConfigMap
 metadata:
   name: chaosfs-pd
+  namespace: chaos-testing
   labels:
     app.kubernetes.io/component: webhook
 data:
diff --git a/examples/chaosfs-configmap/tikv-configmap.yaml b/examples/chaosfs-configmap/tikv-configmap.yaml
index 11d50f97..f5041819 100644
--- a/examples/chaosfs-configmap/tikv-configmap.yaml
+++ b/examples/chaosfs-configmap/tikv-configmap.yaml
@@ -3,6 +3,7 @@ apiVersion: v1
 kind: ConfigMap
 metadata:
   name: chaosfs-tikv
+  namespace: chaos-testing
   labels:
     app.kubernetes.io/component: webhook
 data:
diff --git a/pkg/utils/selector.go b/pkg/utils/selector.go
index 5d17ada1..469d4944 100644
--- a/pkg/utils/selector.go
+++ b/pkg/utils/selector.go
@@ -110,7 +110,6 @@ func SelectPods(ctx context.Context, c client.Client, selector v1alpha1.Selector
 	if err != nil {
 		return nil, err
 	}
-
 	pods, err = filterByNamespaces(pods, namespaceSelector)
 	if err != nil {
 		return nil, err
@@ -122,7 +121,14 @@ func SelectPods(ctx context.Context, c client.Client, selector v1alpha1.Selector
 	}
 	pods = filterByAnnotations(pods, annotationsSelector)
 
-	pods = filterByPhase(pods, v1.PodRunning)
+	phaseSelector, err := parseSelector(strings.Join(selector.PodPhaseSelectors, ","))
+	if err != nil {
+		return nil, err
+	}
+	pods, err = filterByPhaseSelector(pods, phaseSelector)
+	if err != nil {
+		return nil, err
+	}
 
 	return pods, nil
 }
@@ -167,6 +173,7 @@ func CheckPodMeetSelector(pod v1.Pod, selector v1alpha1.SelectorSpec) (bool, err
 	}
 
 	pods := []v1.Pod{pod}
+
 	namespaceSelector, err := parseSelector(strings.Join(selector.Namespaces, ","))
 	if err != nil {
 		return false, err
@@ -181,8 +188,18 @@ func CheckPodMeetSelector(pod v1.Pod, selector v1alpha1.SelectorSpec) (bool, err
 	if err != nil {
 		return false, err
 	}
+
 	pods = filterByAnnotations(pods, annotationsSelector)
-	pods = filterByPhase(pods, v1.PodRunning)
+
+	phaseSelector, err := parseSelector(strings.Join(selector.PodPhaseSelectors, ","))
+	if err != nil {
+		return false, err
+	}
+	pods, err = filterByPhaseSelector(pods, phaseSelector)
+	if err != nil {
+		return false, err
+	}
+
 	if len(pods) > 0 {
 		return true, nil
 	}
@@ -281,17 +298,57 @@ func filterByAnnotations(pods []v1.Pod, annotations labels.Selector) []v1.Pod {
 	return filteredList
 }
 
-// filterByPhase filters a list of pods by a given PodPhase, e.g. Running.
-func filterByPhase(pods []v1.Pod, phase v1.PodPhase) []v1.Pod {
-	var filteredList []v1.Pod
+// filterByPhaseSet filters a list of pods by a given PodPhase selector.
+func filterByPhaseSelector(pods []v1.Pod, phases labels.Selector) ([]v1.Pod, error) {
+	if phases.Empty() {
+		return pods, nil
+	}
+
+	reqs, _ := phases.Requirements()
+	var (
+		reqIncl []labels.Requirement
+		reqExcl []labels.Requirement
+
+		filteredList []v1.Pod
+	)
+
+	for _, req := range reqs {
+		switch req.Operator() {
+		case selection.Exists:
+			reqIncl = append(reqIncl, req)
+		case selection.DoesNotExist:
+			reqExcl = append(reqExcl, req)
+		default:
+			return nil, fmt.Errorf("unsupported operator: %s", req.Operator())
+		}
+	}
 
 	for _, pod := range pods {
-		if pod.Status.Phase == phase {
+		included := len(reqIncl) == 0
+		selector := labels.Set{string(pod.Status.Phase): ""}
+
+		// include pod if one including requirement matches
+		for _, req := range reqIncl {
+			if req.Matches(selector) {
+				included = true
+				break
+			}
+		}
+
+		// exclude pod if it is filtered out by at least one excluding requirement
+		for _, req := range reqExcl {
+			if !req.Matches(selector) {
+				included = false
+				break
+			}
+		}
+
+		if included {
 			filteredList = append(filteredList, pod)
 		}
 	}
 
-	return filteredList
+	return filteredList, nil
 }
 
 // filterByNamespaces filters a list of pods by a given namespace selector.


Test patch:
diff --git a/pkg/utils/selector_test.go b/pkg/utils/selector_test.go
index d89a5126..b0c16218 100644
--- a/pkg/utils/selector_test.go
+++ b/pkg/utils/selector_test.go
@@ -105,7 +105,7 @@ func TestCheckPodMeetSelector(t *testing.T) {
 	tcs := []TestCase{
 		{
 			name: "meet label",
-			pod:  newPod("t1", v1.PodRunning, metav1.NamespaceDefault, nil, map[string]string{"app": "tikv", "ss": "t1"}),
+			pod:  newPod("t1", v1.PodPending, metav1.NamespaceDefault, nil, map[string]string{"app": "tikv", "ss": "t1"}),
 			selector: v1alpha1.SelectorSpec{
 				LabelSelectors: map[string]string{"app": "tikv"},
 			},
@@ -277,14 +277,14 @@ func TestRandomFixedIndexes(t *testing.T) {
 	}
 }
 
-func TestFilterByPhase(t *testing.T) {
+func TestFilterByPhaseSelector(t *testing.T) {
 	g := NewGomegaWithT(t)
 
 	type TestCase struct {
-		name         string
-		pods         []v1.Pod
-		filterPhase  v1.PodPhase
-		filteredPods []v1.Pod
+		name           string
+		pods           []v1.Pod
+		filterSelector labels.Selector
+		filteredPods   []v1.Pod
 	}
 
 	pods := []v1.Pod{
@@ -294,35 +294,65 @@ func TestFilterByPhase(t *testing.T) {
 		newPod("p4", v1.PodFailed, metav1.NamespaceDefault, nil, nil),
 	}
 
-	tcs := []TestCase{
-		{
-			name:         "filter running pods",
-			pods:         pods,
-			filterPhase:  v1.PodRunning,
-			filteredPods: []v1.Pod{pods[0], pods[1]},
-		},
-		{
-			name:         "filter pending pods",
-			pods:         pods,
-			filterPhase:  v1.PodPending,
-			filteredPods: []v1.Pod{pods[2]},
-		},
-		{
-			name:         "no pods",
-			pods:         []v1.Pod{},
-			filterPhase:  v1.PodPending,
-			filteredPods: nil,
-		},
-		{
-			name:         "filter unknown pods",
-			pods:         pods,
-			filterPhase:  v1.PodUnknown,
-			filteredPods: nil,
-		},
-	}
+	var tcs []TestCase
+
+	runningSelector, err := parseSelector(string(pods[1].Status.Phase))
+	g.Expect(err).ShouldNot(HaveOccurred())
+
+	tcs = append(tcs, TestCase{
+		name:           "filter n2",
+		pods:           pods,
+		filterSelector: runningSelector,
+		filteredPods:   []v1.Pod{pods[0], pods[1]},
+	})
+
+	emptySelector, err := parseSelector("")
+	g.Expect(err).ShouldNot(HaveOccurred())
+	tcs = append(tcs, TestCase{
+		name:           "filter empty selector",
+		pods:           pods,
+		filterSelector: emptySelector,
+		filteredPods:   pods,
+	})
+
+	tcs = append(tcs, TestCase{
+		name:           "filter no pods",
+		pods:           []v1.Pod{},
+		filterSelector: runningSelector,
+		filteredPods:   nil,
+	})
+
+	runningAndPendingSelector, err := parseSelector("Running,Pending")
+	g.Expect(err).ShouldNot(HaveOccurred())
+
+	tcs = append(tcs, TestCase{
+		name:           "filter running and pending",
+		pods:           pods,
+		filterSelector: runningAndPendingSelector,
+		filteredPods:   []v1.Pod{pods[0], pods[1], pods[2]},
+	})
+
+	failedSelector, err := parseSelector("Failed")
+	g.Expect(err).ShouldNot(HaveOccurred())
+
+	tcs = append(tcs, TestCase{
+		name:           "filter failed",
+		pods:           pods,
+		filterSelector: failedSelector,
+		filteredPods:   []v1.Pod{pods[3]},
+	})
+
+	unknownSelector, err := parseSelector("Unknown")
+	g.Expect(err).ShouldNot(HaveOccurred())
+	tcs = append(tcs, TestCase{
+		name:           "filter Unknown",
+		pods:           pods,
+		filterSelector: unknownSelector,
+		filteredPods:   nil,
+	})
 
 	for _, tc := range tcs {
-		g.Expect(filterByPhase(tc.pods, tc.filterPhase)).To(Equal(tc.filteredPods), tc.name)
+		g.Expect(filterByPhaseSelector(tc.pods, tc.filterSelector)).To(Equal(tc.filteredPods), tc.name)
 	}
 }