1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package experiment
17
18 import (
19 "context"
20 "time"
21
22 "github.com/jinzhu/gorm"
23 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24 ctrl "sigs.k8s.io/controller-runtime"
25
26 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
27 "github.com/chaos-mesh/chaos-mesh/pkg/dashboard/core"
28 )
29
30 var log = ctrl.Log.WithName("store/experiment")
31
32
33 func NewStore(db *gorm.DB) core.ExperimentStore {
34 db.AutoMigrate(&core.Experiment{})
35
36 return &experimentStore{db}
37 }
38
39
40 func DeleteIncompleteExperiments(es core.ExperimentStore, _ core.EventStore) {
41 if err := es.DeleteIncompleteExperiments(context.Background()); err != nil && !gorm.IsRecordNotFoundError(err) {
42 log.Error(err, "failed to delete all incomplete experiments")
43 }
44 }
45
46 type experimentStore struct {
47 db *gorm.DB
48 }
49
50
51 func (e *experimentStore) ListMeta(_ context.Context, kind, namespace, name string, archived bool) ([]*core.ExperimentMeta, error) {
52 db := e.db.Table("experiments")
53 experiments := make([]*core.ExperimentMeta, 0)
54 query, args := constructQueryArgs(kind, namespace, name, "")
55
56 if err := db.Where(query, args).Where("archived = ?", archived).Find(&experiments).Error; err != nil && !gorm.IsRecordNotFoundError(err) {
57 return nil, err
58 }
59
60 return experiments, nil
61 }
62
63
64 func (e *experimentStore) FindByUID(_ context.Context, uid string) (*core.Experiment, error) {
65 experiment := new(core.Experiment)
66
67 if err := e.db.Where("uid = ?", uid).First(experiment).Error; err != nil {
68 return nil, err
69 }
70
71 return experiment, nil
72 }
73
74
75 func (e *experimentStore) FindMetaByUID(_ context.Context, uid string) (*core.ExperimentMeta, error) {
76 db := e.db.Table("experiments")
77 experiment := new(core.ExperimentMeta)
78
79 if err := db.Where("uid = ?", uid).First(experiment).Error; err != nil {
80 return nil, err
81 }
82
83 return experiment, nil
84 }
85
86
87 func (e *experimentStore) FindManagedByNamespaceName(_ context.Context, namespace, name string) ([]*core.Experiment, error) {
88 experiments := make([]*core.Experiment, 0)
89 if err := e.db.Model(core.Experiment{}).
90 Find(&experiments, "namespace = ? AND name = ? AND archived = ?", namespace, name, true).Error; err != nil {
91 return nil, err
92 }
93
94 managedExperiments := make([]*core.Experiment, 0)
95 for _, expr := range experiments {
96 meta := &unstructured.Unstructured{}
97 if err := meta.UnmarshalJSON([]byte(expr.Experiment)); err != nil {
98 return nil, err
99 }
100
101 if meta.GetLabels()[v1alpha1.LabelManagedBy] != "" {
102 managedExperiments = append(managedExperiments, expr)
103 }
104 }
105
106 if len(managedExperiments) == 0 {
107 return nil, gorm.ErrRecordNotFound
108 }
109
110 return managedExperiments, nil
111 }
112
113
114 func (e *experimentStore) Set(_ context.Context, experiment *core.Experiment) error {
115 return e.db.Model(core.Experiment{}).Save(experiment).Error
116 }
117
118
119 func (e *experimentStore) Archive(_ context.Context, namespace, name string) error {
120 if err := e.db.Model(core.Experiment{}).
121 Where("namespace = ? AND name = ? AND archived = ?", namespace, name, false).
122 Updates(map[string]interface{}{"archived": true, "finish_time": time.Now()}).Error; err != nil && !gorm.IsRecordNotFoundError(err) {
123 return err
124 }
125
126 return nil
127 }
128
129
130 func (e *experimentStore) Delete(_ context.Context, exp *core.Experiment) error {
131 err := e.db.Table("experiments").Unscoped().Delete(*exp).Error
132 return err
133 }
134
135
136 func (e *experimentStore) DeleteByFinishTime(_ context.Context, ttl time.Duration) error {
137 experiments, err := e.ListMeta(context.Background(), "", "", "", true)
138 if err != nil {
139 return err
140 }
141
142 nowTime := time.Now()
143 for _, exp := range experiments {
144 if exp.FinishTime == nil {
145 log.Error(nil, "finish time is nil when deleting archived experiment records, skip it", "experiment", exp)
146 continue
147 }
148 if exp.FinishTime.Add(ttl).Before(nowTime) {
149 if err := e.db.Table("experiments").Unscoped().Delete(*exp).Error; err != nil {
150 return err
151 }
152 }
153 }
154
155 return nil
156 }
157
158
159 func (e *experimentStore) DeleteByUIDs(_ context.Context, uids []string) error {
160 return e.db.Table("experiments").Where("uid IN (?)", uids).Unscoped().Delete(core.Experiment{}).Error
161 }
162
163
164 func (e *experimentStore) DeleteIncompleteExperiments(_ context.Context) error {
165 return e.db.Where("finish_time IS NULL").Unscoped().Delete(core.Experiment{}).Error
166 }
167
168 func constructQueryArgs(kind, ns, name, uid string) (string, []string) {
169 query := ""
170 args := make([]string, 0)
171
172 if kind != "" {
173 query += "kind = ?"
174 args = append(args, kind)
175 }
176
177 if ns != "" {
178 if len(args) > 0 {
179 query += " AND namespace = ?"
180 } else {
181 query += "namespace = ?"
182 }
183 args = append(args, ns)
184 }
185
186 if name != "" {
187 if len(args) > 0 {
188 query += " AND name = ?"
189 } else {
190 query += "name = ?"
191 }
192 args = append(args, name)
193 }
194
195 if uid != "" {
196 if len(args) > 0 {
197 query += " AND uid = ?"
198 } else {
199 query += "uid = ?"
200 }
201 args = append(args, uid)
202 }
203
204 return query, args
205 }
206