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