...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package condition
17
18 import (
19 "context"
20 "reflect"
21
22 "github.com/go-logr/logr"
23 corev1 "k8s.io/api/core/v1"
24 apierrors "k8s.io/apimachinery/pkg/api/errors"
25 "k8s.io/apimachinery/pkg/runtime"
26 "k8s.io/client-go/tools/record"
27 "k8s.io/client-go/util/retry"
28 ctrl "sigs.k8s.io/controller-runtime"
29 "sigs.k8s.io/controller-runtime/pkg/client"
30
31 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
32 )
33
34
35 type Reconciler struct {
36
37 Object runtime.Object
38
39
40 client.Client
41
42 Recorder record.EventRecorder
43
44 Log logr.Logger
45 }
46
47 type StatusAndReason struct {
48 Status corev1.ConditionStatus
49 Reason string
50 }
51
52 func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
53 obj := r.Object.DeepCopyObject().(v1alpha1.InnerObject)
54 if err := r.Client.Get(ctx, req.NamespacedName, obj); err != nil {
55 if apierrors.IsNotFound(err) {
56 r.Log.Info("chaos not found")
57 } else {
58
59 r.Log.Error(err, "unable to get chaos")
60 }
61 return ctrl.Result{}, nil
62 }
63
64 updateError := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
65 conditionMap := make(map[v1alpha1.ChaosConditionType]StatusAndReason)
66 for _, c := range obj.GetStatus().Conditions {
67 conditionMap[c.Type] = StatusAndReason{
68 Status: c.Status,
69 Reason: c.Reason,
70 }
71 }
72
73 newConditionMap := diffConditions(obj)
74
75 if !reflect.DeepEqual(newConditionMap, conditionMap) {
76 conditions := make([]v1alpha1.ChaosCondition, 0, 5)
77 for k, v := range newConditionMap {
78 conditions = append(conditions, v1alpha1.ChaosCondition{
79 Type: k,
80 Status: v.Status,
81 Reason: v.Reason,
82 })
83 }
84
85 r.Log.Info("updating conditions", "conditions", conditions)
86 obj := r.Object.DeepCopyObject().(v1alpha1.InnerObject)
87
88 if err := r.Client.Get(ctx, req.NamespacedName, obj); err != nil {
89 r.Log.Error(err, "unable to get chaos")
90 return err
91 }
92
93 obj.GetStatus().Conditions = conditions
94 return r.Client.Update(ctx, obj)
95 }
96
97 return nil
98 })
99
100 if updateError != nil {
101 r.Log.Error(updateError, "fail to update")
102 r.Recorder.Eventf(obj, "Normal", "Failed", "Failed to update conditions: %s", updateError.Error())
103 return ctrl.Result{}, nil
104 }
105
106 return ctrl.Result{}, nil
107 }
108
109 func diffConditions(obj v1alpha1.InnerObject) (newConditionMap map[v1alpha1.ChaosConditionType]StatusAndReason) {
110 records := obj.GetStatus().Experiment.Records
111 newConditionMap = make(map[v1alpha1.ChaosConditionType]StatusAndReason)
112
113 if records != nil {
114 newConditionMap[v1alpha1.ConditionSelected] = StatusAndReason{
115 Status: corev1.ConditionTrue,
116 }
117 } else {
118 newConditionMap[v1alpha1.ConditionSelected] = StatusAndReason{
119 Status: corev1.ConditionFalse,
120 }
121 }
122
123
124 allInjected := corev1.ConditionFalse
125 if records != nil && every(records, func(record *v1alpha1.Record) bool {
126 return record.Phase == v1alpha1.Injected
127 }) {
128 allInjected = corev1.ConditionTrue
129 }
130
131 allRecovered := corev1.ConditionFalse
132 if records != nil && every(records, func(record *v1alpha1.Record) bool {
133 return record.Phase == v1alpha1.NotInjected
134 }) {
135 allRecovered = corev1.ConditionTrue
136 }
137
138 newConditionMap[v1alpha1.ConditionAllInjected] = StatusAndReason{
139 Status: allInjected,
140 }
141 newConditionMap[v1alpha1.ConditionAllRecovered] = StatusAndReason{
142 Status: allRecovered,
143 }
144
145 if obj.IsPaused() {
146 newConditionMap[v1alpha1.ConditionPaused] = StatusAndReason{
147 Status: corev1.ConditionTrue,
148 }
149 } else {
150 newConditionMap[v1alpha1.ConditionPaused] = StatusAndReason{
151 Status: corev1.ConditionFalse,
152 }
153 }
154
155 return
156 }
157
158
159
160
161 func every[T any](arr []T, condition func(T) bool) bool {
162 for _, item := range arr {
163 if !condition(item) {
164 return false
165 }
166 }
167 return true
168 }
169