1
2
3
4
5
6
7
8
9
10
11
12
13
14 package v2
15
16 import (
17 "fmt"
18 "net/http"
19 "regexp"
20 "sort"
21 "sync"
22 "time"
23
24 "github.com/go-kit/log"
25 "github.com/go-kit/log/level"
26 "github.com/go-openapi/analysis"
27 "github.com/go-openapi/loads"
28 "github.com/go-openapi/runtime/middleware"
29 "github.com/go-openapi/strfmt"
30 "github.com/prometheus/client_golang/prometheus"
31 prometheus_model "github.com/prometheus/common/model"
32 "github.com/prometheus/common/version"
33 "github.com/rs/cors"
34
35 "github.com/prometheus/alertmanager/api/metrics"
36 open_api_models "github.com/prometheus/alertmanager/api/v2/models"
37 "github.com/prometheus/alertmanager/api/v2/restapi"
38 "github.com/prometheus/alertmanager/api/v2/restapi/operations"
39 alert_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alert"
40 alertgroup_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup"
41 general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general"
42 receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver"
43 silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence"
44 "github.com/prometheus/alertmanager/cluster"
45 "github.com/prometheus/alertmanager/config"
46 "github.com/prometheus/alertmanager/dispatch"
47 "github.com/prometheus/alertmanager/pkg/labels"
48 "github.com/prometheus/alertmanager/provider"
49 "github.com/prometheus/alertmanager/silence"
50 "github.com/prometheus/alertmanager/silence/silencepb"
51 "github.com/prometheus/alertmanager/types"
52 )
53
54
55 type API struct {
56 peer cluster.ClusterPeer
57 silences *silence.Silences
58 alerts provider.Alerts
59 alertGroups groupsFn
60 getAlertStatus getAlertStatusFn
61 uptime time.Time
62
63
64 mtx sync.RWMutex
65
66
67 alertmanagerConfig *config.Config
68 route *dispatch.Route
69 setAlertStatus setAlertStatusFn
70
71 logger log.Logger
72 m *metrics.Alerts
73
74 Handler http.Handler
75 }
76
77 type (
78 groupsFn func(func(*dispatch.Route) bool, func(*types.Alert, time.Time) bool) (dispatch.AlertGroups, map[prometheus_model.Fingerprint][]string)
79 getAlertStatusFn func(prometheus_model.Fingerprint) types.AlertStatus
80 setAlertStatusFn func(prometheus_model.LabelSet)
81 )
82
83
84 func NewAPI(
85 alerts provider.Alerts,
86 gf groupsFn,
87 sf getAlertStatusFn,
88 silences *silence.Silences,
89 peer cluster.ClusterPeer,
90 l log.Logger,
91 r prometheus.Registerer,
92 ) (*API, error) {
93 api := API{
94 alerts: alerts,
95 getAlertStatus: sf,
96 alertGroups: gf,
97 peer: peer,
98 silences: silences,
99 logger: l,
100 m: metrics.NewAlerts("v2", r),
101 uptime: time.Now(),
102 }
103
104
105 swaggerSpec, swaggerSpecAnalysis, err := getSwaggerSpec()
106 if err != nil {
107 return nil, err
108 }
109
110
111 openAPI := operations.NewAlertmanagerAPI(swaggerSpec)
112
113
114
115
116 openAPI.Middleware = func(b middleware.Builder) http.Handler {
117
118 swaggerContext := middleware.NewRoutableContextWithAnalyzedSpec(swaggerSpec, swaggerSpecAnalysis, openAPI, nil)
119 return middleware.Spec("", swaggerSpec.Raw(), swaggerContext.RoutesHandler(b))
120 }
121
122 openAPI.AlertGetAlertsHandler = alert_ops.GetAlertsHandlerFunc(api.getAlertsHandler)
123 openAPI.AlertPostAlertsHandler = alert_ops.PostAlertsHandlerFunc(api.postAlertsHandler)
124 openAPI.AlertgroupGetAlertGroupsHandler = alertgroup_ops.GetAlertGroupsHandlerFunc(api.getAlertGroupsHandler)
125 openAPI.GeneralGetStatusHandler = general_ops.GetStatusHandlerFunc(api.getStatusHandler)
126 openAPI.ReceiverGetReceiversHandler = receiver_ops.GetReceiversHandlerFunc(api.getReceiversHandler)
127 openAPI.SilenceDeleteSilenceHandler = silence_ops.DeleteSilenceHandlerFunc(api.deleteSilenceHandler)
128 openAPI.SilenceGetSilenceHandler = silence_ops.GetSilenceHandlerFunc(api.getSilenceHandler)
129 openAPI.SilenceGetSilencesHandler = silence_ops.GetSilencesHandlerFunc(api.getSilencesHandler)
130 openAPI.SilencePostSilencesHandler = silence_ops.PostSilencesHandlerFunc(api.postSilencesHandler)
131
132 handleCORS := cors.Default().Handler
133 api.Handler = handleCORS(openAPI.Serve(nil))
134
135 return &api, nil
136 }
137
138 func (api *API) requestLogger(req *http.Request) log.Logger {
139 return log.With(api.logger, "path", req.URL.Path, "method", req.Method)
140 }
141
142
143 func (api *API) Update(cfg *config.Config, setAlertStatus setAlertStatusFn) {
144 api.mtx.Lock()
145 defer api.mtx.Unlock()
146
147 api.alertmanagerConfig = cfg
148 api.route = dispatch.NewRoute(cfg.Route, nil)
149 api.setAlertStatus = setAlertStatus
150 }
151
152 func (api *API) getStatusHandler(params general_ops.GetStatusParams) middleware.Responder {
153 api.mtx.RLock()
154 defer api.mtx.RUnlock()
155
156 original := api.alertmanagerConfig.String()
157 uptime := strfmt.DateTime(api.uptime)
158
159 status := open_api_models.ClusterStatusStatusDisabled
160
161 resp := open_api_models.AlertmanagerStatus{
162 Uptime: &uptime,
163 VersionInfo: &open_api_models.VersionInfo{
164 Version: &version.Version,
165 Revision: &version.Revision,
166 Branch: &version.Branch,
167 BuildUser: &version.BuildUser,
168 BuildDate: &version.BuildDate,
169 GoVersion: &version.GoVersion,
170 },
171 Config: &open_api_models.AlertmanagerConfig{
172 Original: &original,
173 },
174 Cluster: &open_api_models.ClusterStatus{
175 Status: &status,
176 Peers: []*open_api_models.PeerStatus{},
177 },
178 }
179
180
181 if api.peer != nil {
182 status := api.peer.Status()
183
184 peers := []*open_api_models.PeerStatus{}
185 for _, n := range api.peer.Peers() {
186 address := n.Address()
187 name := n.Name()
188 peers = append(peers, &open_api_models.PeerStatus{
189 Name: &name,
190 Address: &address,
191 })
192 }
193
194 sort.Slice(peers, func(i, j int) bool {
195 return *peers[i].Name < *peers[j].Name
196 })
197
198 resp.Cluster = &open_api_models.ClusterStatus{
199 Name: api.peer.Name(),
200 Status: &status,
201 Peers: peers,
202 }
203 }
204
205 return general_ops.NewGetStatusOK().WithPayload(&resp)
206 }
207
208 func (api *API) getReceiversHandler(params receiver_ops.GetReceiversParams) middleware.Responder {
209 api.mtx.RLock()
210 defer api.mtx.RUnlock()
211
212 receivers := make([]*open_api_models.Receiver, 0, len(api.alertmanagerConfig.Receivers))
213 for _, r := range api.alertmanagerConfig.Receivers {
214 receivers = append(receivers, &open_api_models.Receiver{Name: &r.Name})
215 }
216
217 return receiver_ops.NewGetReceiversOK().WithPayload(receivers)
218 }
219
220 func (api *API) getAlertsHandler(params alert_ops.GetAlertsParams) middleware.Responder {
221 var (
222 receiverFilter *regexp.Regexp
223
224
225 res = open_api_models.GettableAlerts{}
226 ctx = params.HTTPRequest.Context()
227
228 logger = api.requestLogger(params.HTTPRequest)
229 )
230
231 matchers, err := parseFilter(params.Filter)
232 if err != nil {
233 level.Debug(logger).Log("msg", "Failed to parse matchers", "err", err)
234 return alertgroup_ops.NewGetAlertGroupsBadRequest().WithPayload(err.Error())
235 }
236
237 if params.Receiver != nil {
238 receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$")
239 if err != nil {
240 level.Debug(logger).Log("msg", "Failed to compile receiver regex", "err", err)
241 return alert_ops.
242 NewGetAlertsBadRequest().
243 WithPayload(
244 fmt.Sprintf("failed to parse receiver param: %v", err.Error()),
245 )
246 }
247 }
248
249 alerts := api.alerts.GetPending()
250 defer alerts.Close()
251
252 alertFilter := api.alertFilter(matchers, *params.Silenced, *params.Inhibited, *params.Active)
253 now := time.Now()
254
255 api.mtx.RLock()
256 for a := range alerts.Next() {
257 if err = alerts.Err(); err != nil {
258 break
259 }
260 if err = ctx.Err(); err != nil {
261 break
262 }
263
264 routes := api.route.Match(a.Labels)
265 receivers := make([]string, 0, len(routes))
266 for _, r := range routes {
267 receivers = append(receivers, r.RouteOpts.Receiver)
268 }
269
270 if receiverFilter != nil && !receiversMatchFilter(receivers, receiverFilter) {
271 continue
272 }
273
274 if !alertFilter(a, now) {
275 continue
276 }
277
278 alert := AlertToOpenAPIAlert(a, api.getAlertStatus(a.Fingerprint()), receivers)
279
280 res = append(res, alert)
281 }
282 api.mtx.RUnlock()
283
284 if err != nil {
285 level.Error(logger).Log("msg", "Failed to get alerts", "err", err)
286 return alert_ops.NewGetAlertsInternalServerError().WithPayload(err.Error())
287 }
288 sort.Slice(res, func(i, j int) bool {
289 return *res[i].Fingerprint < *res[j].Fingerprint
290 })
291
292 return alert_ops.NewGetAlertsOK().WithPayload(res)
293 }
294
295 func (api *API) postAlertsHandler(params alert_ops.PostAlertsParams) middleware.Responder {
296 logger := api.requestLogger(params.HTTPRequest)
297
298 alerts := OpenAPIAlertsToAlerts(params.Alerts)
299 now := time.Now()
300
301 api.mtx.RLock()
302 resolveTimeout := time.Duration(api.alertmanagerConfig.Global.ResolveTimeout)
303 api.mtx.RUnlock()
304
305 for _, alert := range alerts {
306 alert.UpdatedAt = now
307
308
309 if alert.StartsAt.IsZero() {
310 if alert.EndsAt.IsZero() {
311 alert.StartsAt = now
312 } else {
313 alert.StartsAt = alert.EndsAt
314 }
315 }
316
317
318 if alert.EndsAt.IsZero() {
319 alert.Timeout = true
320 alert.EndsAt = now.Add(resolveTimeout)
321 }
322 if alert.EndsAt.After(time.Now()) {
323 api.m.Firing().Inc()
324 } else {
325 api.m.Resolved().Inc()
326 }
327 }
328
329
330 var (
331 validAlerts = make([]*types.Alert, 0, len(alerts))
332 validationErrs = &types.MultiError{}
333 )
334 for _, a := range alerts {
335 removeEmptyLabels(a.Labels)
336
337 if err := a.Validate(); err != nil {
338 validationErrs.Add(err)
339 api.m.Invalid().Inc()
340 continue
341 }
342 validAlerts = append(validAlerts, a)
343 }
344 if err := api.alerts.Put(validAlerts...); err != nil {
345 level.Error(logger).Log("msg", "Failed to create alerts", "err", err)
346 return alert_ops.NewPostAlertsInternalServerError().WithPayload(err.Error())
347 }
348
349 if validationErrs.Len() > 0 {
350 level.Error(logger).Log("msg", "Failed to validate alerts", "err", validationErrs.Error())
351 return alert_ops.NewPostAlertsBadRequest().WithPayload(validationErrs.Error())
352 }
353
354 return alert_ops.NewPostAlertsOK()
355 }
356
357 func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams) middleware.Responder {
358 logger := api.requestLogger(params.HTTPRequest)
359
360 matchers, err := parseFilter(params.Filter)
361 if err != nil {
362 level.Debug(logger).Log("msg", "Failed to parse matchers", "err", err)
363 return alertgroup_ops.NewGetAlertGroupsBadRequest().WithPayload(err.Error())
364 }
365
366 var receiverFilter *regexp.Regexp
367 if params.Receiver != nil {
368 receiverFilter, err = regexp.Compile("^(?:" + *params.Receiver + ")$")
369 if err != nil {
370 level.Error(logger).Log("msg", "Failed to compile receiver regex", "err", err)
371 return alertgroup_ops.
372 NewGetAlertGroupsBadRequest().
373 WithPayload(
374 fmt.Sprintf("failed to parse receiver param: %v", err.Error()),
375 )
376 }
377 }
378
379 rf := func(receiverFilter *regexp.Regexp) func(r *dispatch.Route) bool {
380 return func(r *dispatch.Route) bool {
381 receiver := r.RouteOpts.Receiver
382 if receiverFilter != nil && !receiverFilter.MatchString(receiver) {
383 return false
384 }
385 return true
386 }
387 }(receiverFilter)
388
389 af := api.alertFilter(matchers, *params.Silenced, *params.Inhibited, *params.Active)
390 alertGroups, allReceivers := api.alertGroups(rf, af)
391
392 res := make(open_api_models.AlertGroups, 0, len(alertGroups))
393
394 for _, alertGroup := range alertGroups {
395 ag := &open_api_models.AlertGroup{
396 Receiver: &open_api_models.Receiver{Name: &alertGroup.Receiver},
397 Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels),
398 Alerts: make([]*open_api_models.GettableAlert, 0, len(alertGroup.Alerts)),
399 }
400
401 for _, alert := range alertGroup.Alerts {
402 fp := alert.Fingerprint()
403 receivers := allReceivers[fp]
404 status := api.getAlertStatus(fp)
405 apiAlert := AlertToOpenAPIAlert(alert, status, receivers)
406 ag.Alerts = append(ag.Alerts, apiAlert)
407 }
408 res = append(res, ag)
409 }
410
411 return alertgroup_ops.NewGetAlertGroupsOK().WithPayload(res)
412 }
413
414 func (api *API) alertFilter(matchers []*labels.Matcher, silenced, inhibited, active bool) func(a *types.Alert, now time.Time) bool {
415 return func(a *types.Alert, now time.Time) bool {
416 if !a.EndsAt.IsZero() && a.EndsAt.Before(now) {
417 return false
418 }
419
420
421 api.setAlertStatus(a.Labels)
422
423
424 status := api.getAlertStatus(a.Fingerprint())
425
426 if !active && status.State == types.AlertStateActive {
427 return false
428 }
429
430 if !silenced && len(status.SilencedBy) != 0 {
431 return false
432 }
433
434 if !inhibited && len(status.InhibitedBy) != 0 {
435 return false
436 }
437
438 return alertMatchesFilterLabels(&a.Alert, matchers)
439 }
440 }
441
442 func removeEmptyLabels(ls prometheus_model.LabelSet) {
443 for k, v := range ls {
444 if string(v) == "" {
445 delete(ls, k)
446 }
447 }
448 }
449
450 func receiversMatchFilter(receivers []string, filter *regexp.Regexp) bool {
451 for _, r := range receivers {
452 if filter.MatchString(r) {
453 return true
454 }
455 }
456
457 return false
458 }
459
460 func alertMatchesFilterLabels(a *prometheus_model.Alert, matchers []*labels.Matcher) bool {
461 sms := make(map[string]string)
462 for name, value := range a.Labels {
463 sms[string(name)] = string(value)
464 }
465 return matchFilterLabels(matchers, sms)
466 }
467
468 func matchFilterLabels(matchers []*labels.Matcher, sms map[string]string) bool {
469 for _, m := range matchers {
470 v, prs := sms[m.Name]
471 switch m.Type {
472 case labels.MatchNotRegexp, labels.MatchNotEqual:
473 if m.Value == "" && prs {
474 continue
475 }
476 if !m.Matches(v) {
477 return false
478 }
479 default:
480 if m.Value == "" && !prs {
481 continue
482 }
483 if !m.Matches(v) {
484 return false
485 }
486 }
487 }
488
489 return true
490 }
491
492 func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middleware.Responder {
493 logger := api.requestLogger(params.HTTPRequest)
494
495 matchers := []*labels.Matcher{}
496 if params.Filter != nil {
497 for _, matcherString := range params.Filter {
498 matcher, err := labels.ParseMatcher(matcherString)
499 if err != nil {
500 level.Debug(logger).Log("msg", "Failed to parse matchers", "err", err)
501 return alert_ops.NewGetAlertsBadRequest().WithPayload(err.Error())
502 }
503
504 matchers = append(matchers, matcher)
505 }
506 }
507
508 psils, _, err := api.silences.Query()
509 if err != nil {
510 level.Error(logger).Log("msg", "Failed to get silences", "err", err)
511 return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error())
512 }
513
514 sils := open_api_models.GettableSilences{}
515 for _, ps := range psils {
516 if !CheckSilenceMatchesFilterLabels(ps, matchers) {
517 continue
518 }
519 silence, err := GettableSilenceFromProto(ps)
520 if err != nil {
521 level.Error(logger).Log("msg", "Failed to unmarshal silence from proto", "err", err)
522 return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error())
523 }
524 sils = append(sils, &silence)
525 }
526
527 SortSilences(sils)
528
529 return silence_ops.NewGetSilencesOK().WithPayload(sils)
530 }
531
532 var silenceStateOrder = map[types.SilenceState]int{
533 types.SilenceStateActive: 1,
534 types.SilenceStatePending: 2,
535 types.SilenceStateExpired: 3,
536 }
537
538
539
540
541
542
543 func SortSilences(sils open_api_models.GettableSilences) {
544 sort.Slice(sils, func(i, j int) bool {
545 state1 := types.SilenceState(*sils[i].Status.State)
546 state2 := types.SilenceState(*sils[j].Status.State)
547 if state1 != state2 {
548 return silenceStateOrder[state1] < silenceStateOrder[state2]
549 }
550 switch state1 {
551 case types.SilenceStateActive:
552 endsAt1 := time.Time(*sils[i].Silence.EndsAt)
553 endsAt2 := time.Time(*sils[j].Silence.EndsAt)
554 return endsAt1.Before(endsAt2)
555 case types.SilenceStatePending:
556 startsAt1 := time.Time(*sils[i].Silence.StartsAt)
557 startsAt2 := time.Time(*sils[j].Silence.StartsAt)
558 return startsAt1.Before(startsAt2)
559 case types.SilenceStateExpired:
560 endsAt1 := time.Time(*sils[i].Silence.EndsAt)
561 endsAt2 := time.Time(*sils[j].Silence.EndsAt)
562 return endsAt1.After(endsAt2)
563 }
564 return false
565 })
566 }
567
568
569
570
571
572
573 func CheckSilenceMatchesFilterLabels(s *silencepb.Silence, matchers []*labels.Matcher) bool {
574 for _, matcher := range matchers {
575 found := false
576 for _, m := range s.Matchers {
577 if matcher.Name == m.Name &&
578 (matcher.Type == labels.MatchEqual && m.Type == silencepb.Matcher_EQUAL ||
579 matcher.Type == labels.MatchRegexp && m.Type == silencepb.Matcher_REGEXP ||
580 matcher.Type == labels.MatchNotEqual && m.Type == silencepb.Matcher_NOT_EQUAL ||
581 matcher.Type == labels.MatchNotRegexp && m.Type == silencepb.Matcher_NOT_REGEXP) &&
582 matcher.Value == m.Pattern {
583 found = true
584 break
585 }
586 }
587 if !found {
588 return false
589 }
590 }
591
592 return true
593 }
594
595 func (api *API) getSilenceHandler(params silence_ops.GetSilenceParams) middleware.Responder {
596 logger := api.requestLogger(params.HTTPRequest)
597
598 sils, _, err := api.silences.Query(silence.QIDs(params.SilenceID.String()))
599 if err != nil {
600 level.Error(logger).Log("msg", "Failed to get silence by id", "err", err, "id", params.SilenceID.String())
601 return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error())
602 }
603
604 if len(sils) == 0 {
605 level.Error(logger).Log("msg", "Failed to find silence", "err", err, "id", params.SilenceID.String())
606 return silence_ops.NewGetSilenceNotFound()
607 }
608
609 sil, err := GettableSilenceFromProto(sils[0])
610 if err != nil {
611 level.Error(logger).Log("msg", "Failed to convert unmarshal from proto", "err", err)
612 return silence_ops.NewGetSilenceInternalServerError().WithPayload(err.Error())
613 }
614
615 return silence_ops.NewGetSilenceOK().WithPayload(&sil)
616 }
617
618 func (api *API) deleteSilenceHandler(params silence_ops.DeleteSilenceParams) middleware.Responder {
619 logger := api.requestLogger(params.HTTPRequest)
620
621 sid := params.SilenceID.String()
622 if err := api.silences.Expire(sid); err != nil {
623 level.Error(logger).Log("msg", "Failed to expire silence", "err", err)
624 return silence_ops.NewDeleteSilenceInternalServerError().WithPayload(err.Error())
625 }
626 return silence_ops.NewDeleteSilenceOK()
627 }
628
629 func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middleware.Responder {
630 logger := api.requestLogger(params.HTTPRequest)
631
632 sil, err := PostableSilenceToProto(params.Silence)
633 if err != nil {
634 level.Error(logger).Log("msg", "Failed to marshal silence to proto", "err", err)
635 return silence_ops.NewPostSilencesBadRequest().WithPayload(
636 fmt.Sprintf("failed to convert API silence to internal silence: %v", err.Error()),
637 )
638 }
639
640 if sil.StartsAt.After(sil.EndsAt) || sil.StartsAt.Equal(sil.EndsAt) {
641 msg := "Failed to create silence: start time must be before end time"
642 level.Error(logger).Log("msg", msg, "starts_at", sil.StartsAt, "ends_at", sil.EndsAt)
643 return silence_ops.NewPostSilencesBadRequest().WithPayload(msg)
644 }
645
646 if sil.EndsAt.Before(time.Now()) {
647 msg := "Failed to create silence: end time can't be in the past"
648 level.Error(logger).Log("msg", msg, "ends_at", sil.EndsAt)
649 return silence_ops.NewPostSilencesBadRequest().WithPayload(msg)
650 }
651
652 sid, err := api.silences.Set(sil)
653 if err != nil {
654 level.Error(logger).Log("msg", "Failed to create silence", "err", err)
655 if err == silence.ErrNotFound {
656 return silence_ops.NewPostSilencesNotFound().WithPayload(err.Error())
657 }
658 return silence_ops.NewPostSilencesBadRequest().WithPayload(err.Error())
659 }
660
661 return silence_ops.NewPostSilencesOK().WithPayload(&silence_ops.PostSilencesOKBody{
662 SilenceID: sid,
663 })
664 }
665
666 func parseFilter(filter []string) ([]*labels.Matcher, error) {
667 matchers := make([]*labels.Matcher, 0, len(filter))
668 for _, matcherString := range filter {
669 matcher, err := labels.ParseMatcher(matcherString)
670 if err != nil {
671 return nil, err
672 }
673
674 matchers = append(matchers, matcher)
675 }
676 return matchers, nil
677 }
678
679 var (
680 swaggerSpecCacheMx sync.Mutex
681 swaggerSpecCache *loads.Document
682 swaggerSpecAnalysisCache *analysis.Spec
683 )
684
685
686
687
688
689 func getSwaggerSpec() (*loads.Document, *analysis.Spec, error) {
690 swaggerSpecCacheMx.Lock()
691 defer swaggerSpecCacheMx.Unlock()
692
693
694 if swaggerSpecCache != nil {
695 return swaggerSpecCache, swaggerSpecAnalysisCache, nil
696 }
697
698
699 swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
700 if err != nil {
701 return nil, nil, fmt.Errorf("failed to load embedded swagger file: %w", err)
702 }
703
704 swaggerSpecCache = swaggerSpec
705 swaggerSpecAnalysisCache = analysis.New(swaggerSpec.Spec())
706 return swaggerSpec, swaggerSpecAnalysisCache, nil
707 }
708
View as plain text