1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package archive
17
18 import (
19 "context"
20 "encoding/json"
21 "fmt"
22 "net/http"
23 "reflect"
24 "strings"
25 "time"
26
27 "github.com/gin-gonic/gin"
28 "github.com/jinzhu/gorm"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
31 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
32 config "github.com/chaos-mesh/chaos-mesh/pkg/config/dashboard"
33 u "github.com/chaos-mesh/chaos-mesh/pkg/dashboard/apiserver/utils"
34 "github.com/chaos-mesh/chaos-mesh/pkg/dashboard/core"
35 )
36
37
38 type Service struct {
39 archive core.ExperimentStore
40 archiveSchedule core.ScheduleStore
41 event core.EventStore
42 workflowStore core.WorkflowStore
43 conf *config.ChaosDashboardConfig
44 }
45
46 func NewService(
47 archive core.ExperimentStore,
48 archiveSchedule core.ScheduleStore,
49 event core.EventStore,
50 workflowStore core.WorkflowStore,
51 conf *config.ChaosDashboardConfig,
52 ) *Service {
53 return &Service{
54 archive: archive,
55 archiveSchedule: archiveSchedule,
56 event: event,
57 workflowStore: workflowStore,
58 conf: conf,
59 }
60 }
61
62
63 func Register(r *gin.RouterGroup, s *Service) {
64 endpoint := r.Group("/archives")
65 endpoint.Use(func(c *gin.Context) {
66 u.AuthMiddleware(c, s.conf)
67 })
68
69 endpoint.GET("", s.list)
70 endpoint.GET("/:uid", s.get)
71 endpoint.DELETE("/:uid", s.delete)
72 endpoint.DELETE("", s.batchDelete)
73
74 endpoint.GET("/schedules", s.listSchedule)
75 endpoint.GET("/schedules/:uid", s.detailSchedule)
76 endpoint.DELETE("/schedules/:uid", s.deleteSchedule)
77 endpoint.DELETE("/schedules", s.batchDeleteSchedule)
78
79 endpoint.GET("/workflows", s.listWorkflow)
80 endpoint.GET("/workflows/:uid", s.detailWorkflow)
81 endpoint.DELETE("/workflows/:uid", s.deleteWorkflow)
82 endpoint.DELETE("/workflows", s.batchDeleteWorkflow)
83 }
84
85
86 type Archive = core.ObjectBase
87
88
89 type Detail struct {
90 Archive
91 KubeObject core.KubeObjectDesc `json:"kube_object"`
92 }
93
94
95
96
97
98
99
100
101
102
103
104 func (s *Service) list(c *gin.Context) {
105 kind := c.Query("kind")
106 name := c.Query("name")
107 ns := c.Query("namespace")
108 if len(ns) == 0 && !s.conf.ClusterScoped &&
109 len(s.conf.TargetNamespace) != 0 {
110 ns = s.conf.TargetNamespace
111 }
112
113 metas, err := s.archive.ListMeta(context.Background(), kind, ns, name, true)
114 if err != nil {
115 c.Status(http.StatusInternalServerError)
116 _ = c.Error(u.ErrInternalServer.NewWithNoMessage())
117 return
118 }
119
120 archives := make([]Archive, 0)
121
122 for _, meta := range metas {
123 archives = append(archives, Archive{
124 UID: meta.UID,
125 Kind: meta.Kind,
126 Namespace: meta.Namespace,
127 Name: meta.Name,
128 Created: meta.StartTime.Format(time.RFC3339),
129 })
130 }
131
132 c.JSON(http.StatusOK, archives)
133 }
134
135
136
137
138
139
140
141
142
143
144 func (s *Service) get(c *gin.Context) {
145 uid := c.Param("uid")
146 exp, err := s.archive.FindByUID(context.Background(), uid)
147 if err != nil {
148 if gorm.IsRecordNotFoundError(err) {
149 u.SetAPIError(c, u.ErrNotFound.New("Experiment "+uid+" not found"))
150 } else {
151 u.SetAPIError(c, u.ErrInternalServer.WrapWithNoMessage(err))
152 }
153
154 return
155 }
156
157 chaos := v1alpha1.AllKinds()[exp.Kind].SpawnObject()
158 _ = json.Unmarshal([]byte(exp.Experiment), chaos)
159
160 c.JSON(http.StatusOK, &Detail{
161 Archive: Archive{
162 UID: exp.UID,
163 Kind: exp.Kind,
164 Name: exp.Name,
165 Namespace: exp.Namespace,
166 Created: exp.StartTime.Format(time.RFC3339),
167 },
168 KubeObject: core.KubeObjectDesc{
169 TypeMeta: metav1.TypeMeta{
170 APIVersion: reflect.ValueOf(chaos).Elem().FieldByName("APIVersion").String(),
171 Kind: reflect.ValueOf(chaos).Elem().FieldByName("Kind").String(),
172 },
173 Meta: core.KubeObjectMeta{
174 Namespace: reflect.ValueOf(chaos).Elem().FieldByName("Namespace").String(),
175 Name: reflect.ValueOf(chaos).Elem().FieldByName("Name").String(),
176 Labels: reflect.ValueOf(chaos).Elem().FieldByName("Labels").Interface().(map[string]string),
177 Annotations: reflect.ValueOf(chaos).Elem().FieldByName("Annotations").Interface().(map[string]string),
178 },
179 Spec: reflect.ValueOf(chaos).Elem().FieldByName("Spec").Interface(),
180 },
181 })
182 }
183
184
185
186
187
188
189
190
191
192 func (s *Service) delete(c *gin.Context) {
193 var (
194 err error
195 exp *core.Experiment
196 )
197
198 uid := c.Param("uid")
199
200 if exp, err = s.archive.FindByUID(context.Background(), uid); err != nil {
201 if gorm.IsRecordNotFoundError(err) {
202 c.Status(http.StatusInternalServerError)
203 _ = c.Error(u.ErrBadRequest.New("the archived experiment is not found"))
204 } else {
205 c.Status(http.StatusInternalServerError)
206 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
207 }
208 return
209 }
210
211 if err = s.archive.Delete(context.Background(), exp); err != nil {
212 c.Status(http.StatusInternalServerError)
213 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
214 } else {
215 if err = s.event.DeleteByUID(context.Background(), uid); err != nil {
216 c.Status(http.StatusInternalServerError)
217 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
218 } else {
219 c.JSON(http.StatusOK, u.ResponseSuccess)
220 }
221 }
222 }
223
224
225
226
227
228
229
230
231
232 func (s *Service) batchDelete(c *gin.Context) {
233 var (
234 err error
235 uidSlice []string
236 )
237
238 uids := c.Query("uids")
239 if uids == "" {
240 c.Status(http.StatusBadRequest)
241 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(fmt.Errorf("uids cannot be empty")))
242 return
243 }
244 uidSlice = strings.Split(uids, ",")
245
246 if err = s.archive.DeleteByUIDs(context.Background(), uidSlice); err != nil {
247 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
248 c.Status(http.StatusInternalServerError)
249 return
250 }
251 if err = s.event.DeleteByUIDs(context.Background(), uidSlice); err != nil {
252 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
253 c.Status(http.StatusInternalServerError)
254 return
255 }
256
257 c.JSON(http.StatusOK, u.ResponseSuccess)
258 }
259
260
261
262
263
264
265
266
267
268
269 func (s *Service) listSchedule(c *gin.Context) {
270 name := c.Query("name")
271 ns := c.Query("namespace")
272
273 metas, err := s.archiveSchedule.ListMeta(context.Background(), ns, name, true)
274 if err != nil {
275 c.Status(http.StatusInternalServerError)
276 _ = c.Error(u.ErrInternalServer.NewWithNoMessage())
277 return
278 }
279
280 archives := make([]Archive, 0)
281
282 for _, meta := range metas {
283 archives = append(archives, Archive{
284 UID: meta.UID,
285 Kind: meta.Kind,
286 Namespace: meta.Namespace,
287 Name: meta.Name,
288 Created: meta.StartTime.Format(time.RFC3339),
289 })
290 }
291
292 c.JSON(http.StatusOK, archives)
293 }
294
295
296
297
298
299
300
301
302
303 func (s *Service) detailSchedule(c *gin.Context) {
304 var (
305 err error
306 detail Detail
307 )
308 uid := c.Param("uid")
309
310 if uid == "" {
311 c.Status(http.StatusBadRequest)
312 _ = c.Error(u.ErrBadRequest.New("uid cannot be empty"))
313 return
314 }
315
316 exp, err := s.archiveSchedule.FindByUID(context.Background(), uid)
317 if err != nil {
318 if gorm.IsRecordNotFoundError(err) {
319 c.Status(http.StatusInternalServerError)
320 _ = c.Error(u.ErrBadRequest.New("the archive schedule is not found"))
321 } else {
322 c.Status(http.StatusInternalServerError)
323 _ = c.Error(u.ErrInternalServer.NewWithNoMessage())
324 }
325 return
326 }
327
328 sch := &v1alpha1.Schedule{}
329 if err := json.Unmarshal([]byte(exp.Schedule), &sch); err != nil {
330 c.Status(http.StatusInternalServerError)
331 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
332 return
333 }
334
335 detail = Detail{
336 Archive: Archive{
337 UID: exp.UID,
338 Kind: exp.Kind,
339 Name: exp.Name,
340 Namespace: exp.Namespace,
341 Created: exp.StartTime.Format(time.RFC3339),
342 },
343 KubeObject: core.KubeObjectDesc{
344 TypeMeta: metav1.TypeMeta{
345 APIVersion: sch.APIVersion,
346 Kind: sch.Kind,
347 },
348 Meta: core.KubeObjectMeta{
349 Name: sch.Name,
350 Namespace: sch.Namespace,
351 Labels: sch.Labels,
352 Annotations: sch.Annotations,
353 },
354 Spec: sch.Spec,
355 },
356 }
357
358 c.JSON(http.StatusOK, detail)
359 }
360
361
362
363
364
365
366
367
368
369 func (s *Service) deleteSchedule(c *gin.Context) {
370 var (
371 err error
372 exp *core.Schedule
373 )
374
375 uid := c.Param("uid")
376
377 if exp, err = s.archiveSchedule.FindByUID(context.Background(), uid); err != nil {
378 if gorm.IsRecordNotFoundError(err) {
379 c.Status(http.StatusInternalServerError)
380 _ = c.Error(u.ErrBadRequest.New("the archived schedule is not found"))
381 } else {
382 c.Status(http.StatusInternalServerError)
383 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
384 }
385 return
386 }
387
388 if err = s.archiveSchedule.Delete(context.Background(), exp); err != nil {
389 c.Status(http.StatusInternalServerError)
390 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
391 } else {
392 if err = s.event.DeleteByUID(context.Background(), uid); err != nil {
393 c.Status(http.StatusInternalServerError)
394 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
395 } else {
396 c.JSON(http.StatusOK, u.ResponseSuccess)
397 }
398 }
399 }
400
401
402
403
404
405
406
407
408
409 func (s *Service) batchDeleteSchedule(c *gin.Context) {
410 var (
411 err error
412 uidSlice []string
413 )
414
415 uids := c.Query("uids")
416 if uids == "" {
417 c.Status(http.StatusBadRequest)
418 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(fmt.Errorf("uids cannot be empty")))
419 return
420 }
421 uidSlice = strings.Split(uids, ",")
422
423 if err = s.archiveSchedule.DeleteByUIDs(context.Background(), uidSlice); err != nil {
424 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
425 c.Status(http.StatusInternalServerError)
426 return
427 }
428 if err = s.event.DeleteByUIDs(context.Background(), uidSlice); err != nil {
429 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
430 c.Status(http.StatusInternalServerError)
431 return
432 }
433
434 c.JSON(http.StatusOK, u.ResponseSuccess)
435 }
436
437
438
439
440
441
442
443
444
445
446 func (s *Service) listWorkflow(c *gin.Context) {
447 name := c.Query("name")
448 ns := c.Query("namespace")
449
450 metas, err := s.workflowStore.ListMeta(context.Background(), ns, name, true)
451 if err != nil {
452 c.Status(http.StatusInternalServerError)
453 _ = c.Error(u.ErrInternalServer.NewWithNoMessage())
454 return
455 }
456
457 archives := make([]Archive, 0)
458
459 for _, meta := range metas {
460 archives = append(archives, Archive{
461 UID: meta.UID,
462 Kind: v1alpha1.KindWorkflow,
463 Namespace: meta.Namespace,
464 Name: meta.Name,
465 Created: meta.CreatedAt.Format(time.RFC3339),
466 })
467 }
468
469 c.JSON(http.StatusOK, archives)
470 }
471
472
473
474
475
476
477
478
479
480 func (s *Service) detailWorkflow(c *gin.Context) {
481 var (
482 err error
483 detail Detail
484 )
485 uid := c.Param("uid")
486
487 if uid == "" {
488 c.Status(http.StatusBadRequest)
489 _ = c.Error(u.ErrBadRequest.New("uid cannot be empty"))
490 return
491 }
492
493 meta, err := s.workflowStore.FindByUID(context.Background(), uid)
494 if err != nil {
495 if gorm.IsRecordNotFoundError(err) {
496 c.Status(http.StatusInternalServerError)
497 _ = c.Error(u.ErrBadRequest.New("the archive schedule is not found"))
498 } else {
499 c.Status(http.StatusInternalServerError)
500 _ = c.Error(u.ErrInternalServer.NewWithNoMessage())
501 }
502 return
503 }
504
505 workflow := &v1alpha1.Workflow{}
506 if err := json.Unmarshal([]byte(meta.Workflow), &workflow); err != nil {
507 c.Status(http.StatusInternalServerError)
508 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
509 return
510 }
511
512 detail = Detail{
513 Archive: Archive{
514 UID: meta.UID,
515 Kind: v1alpha1.KindWorkflow,
516 Name: meta.Name,
517 Namespace: meta.Namespace,
518 Created: meta.CreatedAt.Format(time.RFC3339),
519 },
520 KubeObject: core.KubeObjectDesc{
521 TypeMeta: metav1.TypeMeta{
522 APIVersion: workflow.APIVersion,
523 Kind: workflow.Kind,
524 },
525 Meta: core.KubeObjectMeta{
526 Name: workflow.Name,
527 Namespace: workflow.Namespace,
528 Labels: workflow.Labels,
529 Annotations: workflow.Annotations,
530 },
531 Spec: workflow.Spec,
532 },
533 }
534
535 c.JSON(http.StatusOK, detail)
536 }
537
538
539
540
541
542
543
544
545
546 func (s *Service) deleteWorkflow(c *gin.Context) {
547 var (
548 err error
549 )
550
551 uid := c.Param("uid")
552
553 if err = s.workflowStore.DeleteByUID(context.Background(), uid); err != nil {
554 c.Status(http.StatusInternalServerError)
555 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
556 } else {
557 if err = s.event.DeleteByUID(context.Background(), uid); err != nil {
558 c.Status(http.StatusInternalServerError)
559 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
560 } else {
561 c.JSON(http.StatusOK, u.ResponseSuccess)
562 }
563 }
564 }
565
566
567
568
569
570
571
572
573
574 func (s *Service) batchDeleteWorkflow(c *gin.Context) {
575 var (
576 err error
577 uidSlice []string
578 )
579
580 uids := c.Query("uids")
581 if uids == "" {
582 c.Status(http.StatusBadRequest)
583 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(fmt.Errorf("uids cannot be empty")))
584 return
585 }
586 uidSlice = strings.Split(uids, ",")
587
588 if err = s.workflowStore.DeleteByUIDs(context.Background(), uidSlice); err != nil {
589 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
590 c.Status(http.StatusInternalServerError)
591 return
592 }
593 if err = s.event.DeleteByUIDs(context.Background(), uidSlice); err != nil {
594 _ = c.Error(u.ErrInternalServer.WrapWithNoMessage(err))
595 c.Status(http.StatusInternalServerError)
596 return
597 }
598
599 c.JSON(http.StatusOK, u.ResponseSuccess)
600 }
601