From cf0796c2290843faa3db005741e23d3085e4c36f Mon Sep 17 00:00:00 2001 From: nvnyale Date: Tue, 25 Oct 2022 01:23:55 -0400 Subject: [PATCH] added metrics for datasync service --- api/handlers_metrics.go | 84 +++++++++++++++++++++++++++++++++++++++++ api/routes.go | 2 + 2 files changed, 86 insertions(+) diff --git a/api/handlers_metrics.go b/api/handlers_metrics.go index 13471da..ffb22ed 100644 --- a/api/handlers_metrics.go +++ b/api/handlers_metrics.go @@ -367,6 +367,90 @@ func (s *server) GetRDSMetricsURLHandler(w http.ResponseWriter, r *http.Request) w.Write(meta) } +func (s *server) GetDataSyncMetricsURLHandler(w http.ResponseWriter, r *http.Request) { + w = LogWriter{w} + vars := mux.Vars(r) + account := s.mapAccountNumber(vars["account"]) + taskId := vars["taskId"] + + policy, err := defaultCloudWatchMetricsPolicy() + if err != nil { + handleError(w, err) + return + } + + role := fmt.Sprintf("arn:aws:iam::%s:role/%s", account, s.session.RoleName) + session, err := s.assumeRole( + r.Context(), + s.session.ExternalID, + role, + policy, + ) + if err != nil { + msg := fmt.Sprintf("failed to assume role in account: %s", account) + handleError(w, apierror.New(apierror.ErrForbidden, msg, nil)) + return + } + + cwService := cloudwatch.New(cloudwatch.WithSession(session.Session)) + + queries := r.URL.Query() + metrics := queries["metric"] + if len(metrics) == 0 { + handleError(w, apierror.New(apierror.ErrBadRequest, "at least one metric is required", nil)) + return + } + + req := cloudwatch.MetricsRequest{} + if err := parseQuery(r, req); err != nil { + handleError(w, apierror.New(apierror.ErrBadRequest, "failed to parse query", err)) + return + } + + key := fmt.Sprintf("%s/%s/%s/%s%s", account, s.org, taskId, strings.Join(metrics, "-"), req.String()) + log.Debugf("object key: %s", key) + + hashedCacheKey := s.imageCache.HashedKey(key) + if res, expire, ok := s.resultCache.GetWithExpiration(hashedCacheKey); ok { + log.Debugf("found cached object: %s", res) + + if body, ok := res.([]byte); ok { + w.Header().Set("X-Cache-Hit", "true") + w.Header().Set("X-Cache-Expire", fmt.Sprintf("%0.fs", time.Until(expire).Seconds())) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(body) + return + } + } + + cwMetrics := []cloudwatch.Metric{} + for _, m := range metrics { + cwMetrics = append(cwMetrics, cloudwatch.Metric{"AWS/DataSync", m, "TaskId", taskId}) + } + req["metrics"] = cwMetrics + + log.Debugf("getting metrics with request %+v", req) + image, err := cwService.GetMetricWidget(r.Context(), req) + if err != nil { + log.Errorf("failed getting metrics widget image: %s", err) + handleError(w, err) + return + } + + meta, err := s.imageCache.Save(r.Context(), hashedCacheKey, image) + if err != nil { + log.Errorf("failed saving metrics widget image to cache: %s", err) + handleError(w, err) + return + } + s.resultCache.Set(hashedCacheKey, meta, 300*time.Second) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(meta) +} + func parseQuery(r *http.Request, request cloudwatch.MetricsRequest) error { log.SetLevel(log.DebugLevel) queries := r.URL.Query() diff --git a/api/routes.go b/api/routes.go index c5c7701..2cae2cd 100644 --- a/api/routes.go +++ b/api/routes.go @@ -39,6 +39,8 @@ func (s *server) routes() { metricsApi.HandleFunc("/{account}/buckets/{bucket}/graph", s.GetS3MetricsURLHandler).Queries("metric", "{metric:(?:BucketSizeBytes|NumberOfObjects)}").Methods(http.MethodGet) // metrics endpoints for RDS services metricsApi.HandleFunc("/{account}/rds/{type}/{id}/graph", s.GetRDSMetricsURLHandler).Methods(http.MethodGet) + // metrics endpoints for DataSync services + metricsApi.HandleFunc("/{account}/movers/{taskId}/graph", s.GetDataSyncMetricsURLHandler).Methods(http.MethodGet) inventoryApi := s.router.PathPrefix("/v1/inventory").Subrouter() inventoryApi.HandleFunc("/ping", s.PingHandler).Methods(http.MethodGet)