1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package template
17
18 import (
19 "context"
20 "net/http"
21 "sort"
22 "time"
23
24 "github.com/gin-gonic/gin"
25 "github.com/go-logr/logr"
26 "gopkg.in/yaml.v2"
27 v1 "k8s.io/api/core/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/types"
30 "sigs.k8s.io/controller-runtime/pkg/client"
31
32 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
33 "github.com/chaos-mesh/chaos-mesh/pkg/clientpool"
34 config "github.com/chaos-mesh/chaos-mesh/pkg/config"
35 apiservertypes "github.com/chaos-mesh/chaos-mesh/pkg/dashboard/apiserver/types"
36 u "github.com/chaos-mesh/chaos-mesh/pkg/dashboard/apiserver/utils"
37 )
38
39
40 type Service struct {
41 conf *config.ChaosDashboardConfig
42 logger logr.Logger
43 }
44
45 func NewService(conf *config.ChaosDashboardConfig, logger logr.Logger) *Service {
46 return &Service{conf: conf, logger: logger}
47 }
48
49 func Register(r *gin.RouterGroup, s *Service) {
50 endpoint := r.Group("/templates")
51
52 statusCheckEndpoint := endpoint.Group("/statuschecks")
53 statusCheckEndpoint.GET("", s.listStatusCheckTemplate)
54 statusCheckEndpoint.POST("", s.createStatusCheckTemplate)
55 statusCheckEndpoint.GET("/statuscheck", s.getStatusCheckTemplateDetail)
56 statusCheckEndpoint.PUT("/statuscheck", s.updateStatusCheckTemplate)
57 statusCheckEndpoint.DELETE("/statuscheck", s.deleteStatusCheckTemplate)
58 }
59
60
61
62
63
64
65
66
67
68
69
70 func (s *Service) listStatusCheckTemplate(c *gin.Context) {
71 kubeCli, err := clientpool.ExtractTokenAndGetClient(c.Request.Header)
72 if err != nil {
73 u.SetAPIError(c, u.ErrBadRequest.WrapWithNoMessage(err))
74 return
75 }
76
77 ns, name := c.Query("namespace"), c.Query("name")
78 if ns == "" && !s.conf.ClusterScoped && s.conf.TargetNamespace != "" {
79 ns = s.conf.TargetNamespace
80 s.logger.Info("Replace query namespace", "ns", ns)
81 }
82
83 configMapList := v1.ConfigMapList{}
84 if err = kubeCli.List(context.Background(), &configMapList,
85 client.InNamespace(ns),
86 client.MatchingLabels{
87 v1alpha1.TemplateTypeLabelKey: v1alpha1.KindStatusCheck,
88 v1alpha1.ManagedByLabelKey: v1alpha1.ManagedByLabelValue,
89 },
90 ); err != nil {
91 u.SetAPImachineryError(c, err)
92 return
93 }
94
95 templates := make([]*apiservertypes.StatusCheckTemplateBase, 0)
96 for _, cm := range configMapList.Items {
97 templateName := v1alpha1.GetTemplateName(cm)
98 if templateName == "" {
99
100 continue
101 }
102 if name != "" && templateName != name {
103 continue
104 }
105
106 templates = append(templates, &apiservertypes.StatusCheckTemplateBase{
107 Namespace: cm.Namespace,
108 Name: templateName,
109 UID: string(cm.UID),
110 Created: cm.CreationTimestamp.Format(time.RFC3339),
111 Description: v1alpha1.GetTemplateDescription(cm),
112 })
113 }
114
115 sort.Slice(templates, func(i, j int) bool {
116 return templates[i].Created > templates[j].Created
117 })
118
119 c.JSON(http.StatusOK, templates)
120 }
121
122
123
124
125
126
127
128
129
130
131
132 func (s *Service) createStatusCheckTemplate(c *gin.Context) {
133 kubeCli, err := clientpool.ExtractTokenAndGetClient(c.Request.Header)
134 if err != nil {
135 u.SetAPIError(c, u.ErrBadRequest.WrapWithNoMessage(err))
136 return
137 }
138
139 var template apiservertypes.StatusCheckTemplate
140 if err = u.ShouldBindBodyWithJSON(c, &template); err != nil {
141 return
142 }
143 template.Spec.Default()
144 if _, err := template.Spec.Validate(); err != nil {
145 u.SetAPIError(c, u.ErrInternalServer.WrapWithNoMessage(err))
146 return
147 }
148
149 spec, err := yaml.Marshal(template.Spec)
150 if err != nil {
151 u.SetAPIError(c, u.ErrInternalServer.WrapWithNoMessage(err))
152 return
153 }
154 cm := v1.ConfigMap{
155 ObjectMeta: metav1.ObjectMeta{
156 Namespace: template.Namespace,
157 Name: v1alpha1.GenerateTemplateName(template.Name),
158 Labels: map[string]string{
159 v1alpha1.TemplateTypeLabelKey: v1alpha1.KindStatusCheck,
160 v1alpha1.ManagedByLabelKey: v1alpha1.ManagedByLabelValue,
161 },
162 Annotations: map[string]string{
163 v1alpha1.TemplateNameAnnotationKey: template.Name,
164 v1alpha1.TemplateDescriptionAnnotationKey: template.Description,
165 },
166 },
167 Data: map[string]string{v1alpha1.StatusCheckTemplateKey: string(spec)},
168 }
169 if err = kubeCli.Create(context.Background(), &cm); err != nil {
170 u.SetAPImachineryError(c, err)
171 return
172 }
173
174 c.JSON(http.StatusOK, template)
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188 func (s *Service) getStatusCheckTemplateDetail(c *gin.Context) {
189 kubeCli, err := clientpool.ExtractTokenAndGetClient(c.Request.Header)
190 if err != nil {
191 u.SetAPIError(c, u.ErrBadRequest.WrapWithNoMessage(err))
192 return
193 }
194
195 ns, name := c.Query("namespace"), c.Query("name")
196 if ns == "" && !s.conf.ClusterScoped && s.conf.TargetNamespace != "" {
197 ns = s.conf.TargetNamespace
198 s.logger.Info("Replace query namespace", "ns", ns)
199 }
200 if name == "" {
201 u.SetAPIError(c, u.ErrBadRequest.New("name is required"))
202 return
203 }
204
205 var cm v1.ConfigMap
206 if err = kubeCli.Get(context.Background(),
207 types.NamespacedName{
208 Namespace: ns,
209 Name: v1alpha1.GenerateTemplateName(name),
210 }, &cm); err != nil {
211 u.SetAPImachineryError(c, err)
212 return
213 }
214
215 if !v1alpha1.IsStatusCheckTemplate(cm) {
216 u.SetAPIError(c, u.ErrInternalServer.New("invalid status check template"))
217 return
218 }
219
220 var spec v1alpha1.StatusCheckTemplate
221 if err := yaml.Unmarshal([]byte(cm.Data[v1alpha1.StatusCheckTemplateKey]), &spec); err != nil {
222 u.SetAPIError(c, u.ErrInternalServer.WrapWithNoMessage(err))
223 return
224 }
225 detail := apiservertypes.StatusCheckTemplateDetail{
226 StatusCheckTemplateBase: apiservertypes.StatusCheckTemplateBase{
227 Namespace: cm.Namespace,
228 Name: v1alpha1.GetTemplateName(cm),
229 UID: string(cm.UID),
230 Created: cm.CreationTimestamp.Format(time.RFC3339),
231 Description: v1alpha1.GetTemplateDescription(cm),
232 },
233 Spec: spec,
234 }
235 c.JSON(http.StatusOK, detail)
236 }
237
238
239
240
241
242
243
244
245
246
247 func (s *Service) updateStatusCheckTemplate(c *gin.Context) {
248 kubeCli, err := clientpool.ExtractTokenAndGetClient(c.Request.Header)
249 if err != nil {
250 u.SetAPIError(c, u.ErrBadRequest.WrapWithNoMessage(err))
251 return
252 }
253
254 var template apiservertypes.StatusCheckTemplate
255 if err = u.ShouldBindBodyWithJSON(c, &template); err != nil {
256 return
257 }
258 template.Spec.Default()
259 if _, err := template.Spec.Validate(); err != nil {
260 u.SetAPIError(c, u.ErrInternalServer.WrapWithNoMessage(err))
261 return
262 }
263
264 var cm v1.ConfigMap
265 if err = kubeCli.Get(context.Background(),
266 types.NamespacedName{
267 Namespace: template.Namespace,
268 Name: v1alpha1.GenerateTemplateName(template.Name),
269 }, &cm); err != nil {
270 u.SetAPImachineryError(c, err)
271 return
272 }
273 if !v1alpha1.IsStatusCheckTemplate(cm) {
274 u.SetAPIError(c, u.ErrInternalServer.New("invalid status check template"))
275 return
276 }
277
278 spec, err := yaml.Marshal(template.Spec)
279 if err != nil {
280 u.SetAPIError(c, u.ErrInternalServer.WrapWithNoMessage(err))
281 return
282 }
283 if cm.Data == nil {
284 cm.Data = map[string]string{v1alpha1.StatusCheckTemplateKey: string(spec)}
285 } else {
286 cm.Data[v1alpha1.StatusCheckTemplateKey] = string(spec)
287 }
288 cm.Annotations[v1alpha1.TemplateDescriptionAnnotationKey] = template.Description
289
290 if err := kubeCli.Update(context.Background(), &cm); err != nil {
291 u.SetAPImachineryError(c, err)
292 return
293 }
294 c.JSON(http.StatusOK, template)
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308 func (s *Service) deleteStatusCheckTemplate(c *gin.Context) {
309 kubeCli, err := clientpool.ExtractTokenAndGetClient(c.Request.Header)
310 if err != nil {
311 u.SetAPIError(c, u.ErrBadRequest.WrapWithNoMessage(err))
312 return
313 }
314
315 ns, name := c.Query("namespace"), c.Query("name")
316 if ns == "" && !s.conf.ClusterScoped && s.conf.TargetNamespace != "" {
317 ns = s.conf.TargetNamespace
318 s.logger.Info("Replace query namespace", "ns", ns)
319 }
320 if name == "" {
321 u.SetAPIError(c, u.ErrBadRequest.New("name is required"))
322 return
323 }
324
325 var cm v1.ConfigMap
326 if err = kubeCli.Get(context.Background(),
327 types.NamespacedName{
328 Namespace: ns,
329 Name: v1alpha1.GenerateTemplateName(name),
330 }, &cm); err != nil {
331 u.SetAPImachineryError(c, err)
332 return
333 }
334 if !v1alpha1.IsStatusCheckTemplate(cm) {
335 u.SetAPIError(c, u.ErrInternalServer.New("invalid status check template"))
336 return
337 }
338
339 if err := kubeCli.Delete(context.Background(), &cm); err != nil {
340 u.SetAPImachineryError(c, err)
341 return
342 }
343 c.JSON(http.StatusOK, u.ResponseSuccess)
344 }
345