1
2
3
4
5
6
7
8
9
10
11
12
13
14 package gc
15
16 import (
17 "context"
18 "fmt"
19 "reflect"
20 "sort"
21 "time"
22
23 "github.com/go-logr/logr"
24 "go.uber.org/fx"
25 corev1 "k8s.io/api/core/v1"
26 k8sError "k8s.io/apimachinery/pkg/api/errors"
27 "k8s.io/apimachinery/pkg/runtime"
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 "github.com/chaos-mesh/chaos-mesh/controllers/schedule/utils"
33 "github.com/chaos-mesh/chaos-mesh/controllers/types"
34 "github.com/chaos-mesh/chaos-mesh/controllers/utils/builder"
35 "github.com/chaos-mesh/chaos-mesh/controllers/utils/controller"
36 "github.com/chaos-mesh/chaos-mesh/controllers/utils/recorder"
37 "github.com/chaos-mesh/chaos-mesh/pkg/workflow/controllers"
38 )
39
40 type Reconciler struct {
41 client.Client
42 Log logr.Logger
43 Recorder recorder.ChaosRecorder
44
45 ActiveLister *utils.ActiveLister
46 }
47
48 func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
49 ctx := context.Background()
50
51
52
53 schedule := &v1alpha1.Schedule{}
54 err := r.Get(ctx, req.NamespacedName, schedule)
55 if err != nil {
56 if !k8sError.IsNotFound(err) {
57 r.Log.Error(err, "unable to get chaos")
58 }
59 return ctrl.Result{}, nil
60 }
61
62 list, err := r.ActiveLister.ListActiveJobs(ctx, schedule)
63 if err != nil {
64 r.Recorder.Event(schedule, recorder.Failed{
65 Activity: "list active jobs",
66 Err: err.Error(),
67 })
68 return ctrl.Result{}, nil
69 }
70
71 items := reflect.ValueOf(list).Elem().FieldByName("Items")
72 metaItems := []v1alpha1.MetaObject{}
73 for i := 0; i < items.Len(); i++ {
74 item := items.Index(i).Addr().Interface().(v1alpha1.MetaObject)
75 metaItems = append(metaItems, item)
76 }
77
78 sort.Slice(metaItems, func(x, y int) bool {
79 return metaItems[x].GetObjectMeta().CreationTimestamp.Time.Before(metaItems[y].GetObjectMeta().CreationTimestamp.Time)
80 })
81
82 exceededHistory := len(metaItems) - schedule.Spec.HistoryLimit
83 requeuAfter := time.Duration(0)
84 if exceededHistory > 0 {
85 for _, obj := range metaItems[0:exceededHistory] {
86 innerObj, ok := obj.(v1alpha1.InnerObject)
87 if ok {
88 finished, untilStop := controller.IsChaosFinishedWithUntilStop(innerObj, time.Now())
89
90 if !finished {
91 if untilStop != 0 {
92 if requeuAfter == 0 || requeuAfter > untilStop {
93 requeuAfter = untilStop
94 }
95
96 r.Recorder.Event(schedule, recorder.ScheduleSkipRemoveHistory{
97 RunningName: innerObj.GetChaos().Name,
98 })
99 continue
100 }
101
102
103 r.Log.Info("untilStop is 0 when the chaos has not finished")
104 }
105 } else {
106 if schedule.Spec.Type == v1alpha1.ScheduleTypeWorkflow {
107 workflow, ok := obj.(*v1alpha1.Workflow)
108 if ok {
109 finished := controllers.WorkflowConditionEqualsTo(workflow.Status, v1alpha1.WorkflowConditionAccomplished, corev1.ConditionTrue)
110
111 if !finished {
112 r.Recorder.Event(schedule, recorder.ScheduleSkipRemoveHistory{
113 RunningName: workflow.Name,
114 })
115 continue
116 }
117 }
118 }
119 }
120 err := r.Client.Delete(ctx, obj)
121 if err != nil && !k8sError.IsNotFound(err) {
122 r.Recorder.Event(schedule, recorder.Failed{
123 Activity: fmt.Sprintf("delete %s/%s", obj.GetObjectMeta().Namespace, obj.GetObjectMeta().Name),
124 Err: err.Error(),
125 })
126 }
127 }
128 }
129
130 return ctrl.Result{
131 RequeueAfter: requeuAfter,
132 }, nil
133 }
134
135 type Objs struct {
136 fx.In
137
138 ScheduleObjs []types.Object `group:"schedule"`
139 Objs []types.Object `group:"objs"`
140 }
141
142 func NewController(mgr ctrl.Manager, client client.Client, log logr.Logger, objs Objs, scheme *runtime.Scheme, lister *utils.ActiveLister, recorderBuilder *recorder.RecorderBuilder) (types.Controller, error) {
143 builder := builder.Default(mgr).
144 For(&v1alpha1.Schedule{}).
145 Named("schedule-gc")
146
147 for _, obj := range objs.Objs {
148
149 builder.Owns(obj.Object)
150 }
151
152 builder = builder.Owns(&v1alpha1.Workflow{})
153
154 builder.Complete(&Reconciler{
155 client,
156 log.WithName("schedule-gc"),
157 recorderBuilder.Build("schedule-gc"),
158 lister,
159 })
160 return "schedule-gc", nil
161 }
162