diff --git a/db-connector.go b/db-connector.go index 7b14be56..af3759bf 100755 --- a/db-connector.go +++ b/db-connector.go @@ -2849,6 +2849,61 @@ func GetOrg(ctx context.Context, id string) (*Org, error) { return curOrg, nil } +func GetFirstOrg(ctx context.Context) (*Org, error) { + nameKey := "Organizations" + + curOrg := &Org{} + if project.DbType == "opensearch" { + res, err := project.Es.Search( + project.Es.Search.WithContext(ctx), + project.Es.Search.WithIndex(strings.ToLower(GetESIndexPrefix(nameKey))), + project.Es.Search.WithTrackTotalHits(true), + ) + if err != nil { + log.Printf("[ERROR] Error getting response from Opensearch (get first org): %s", err) + return curOrg, err + } + + defer res.Body.Close() + if res.StatusCode != 200 && res.StatusCode != 201 { + return curOrg, errors.New(fmt.Sprintf("Bad statuscode: %d", res.StatusCode)) + } + + respBody, err := ioutil.ReadAll(res.Body) + if err != nil { + return curOrg, err + } + + wrapped := OrgSearchWrapper{} + err = json.Unmarshal(respBody, &wrapped) + if err != nil { + return curOrg, err + } + + if len(wrapped.Hits.Hits) > 0 { + curOrg = &wrapped.Hits.Hits[0].Source + } else { + return curOrg, errors.New("No orgs found") + } + + } else { + query := datastore.NewQuery(nameKey).Limit(1) + allOrgs := []Org{} + _, err := project.Dbclient.GetAll(ctx, query, &allOrgs) + if err != nil { + return curOrg, err + } + + if len(allOrgs) > 0 { + curOrg = &allOrgs[0] + } else { + return curOrg, errors.New("No orgs found") + } + } + + return curOrg, nil +} + func indexEs(ctx context.Context, nameKey, id string, bytes []byte) error { //req := esapi.IndexRequest{ req := opensearchapi.IndexRequest{ @@ -3849,114 +3904,6 @@ func GetUser(ctx context.Context, username string) (*User, error) { return curUser, nil } -func GetOpsDashboardCacheHitStat(ctx context.Context, limit int) ([]OpsDashboardStats, error) { - // get last 30 runs - nameKey := "opsdashboardstats" - var stats []OpsDashboardStats - - if project.DbType == "opensearch" { - var buf bytes.Buffer - query := map[string]interface{}{ - "size": limit, - "sort": map[string]interface{}{ - "timestamp": map[string]interface{}{ - "order": "desc", - }, - }, - } - - if err := json.NewEncoder(&buf).Encode(query); err != nil { - log.Printf("[WARNING] Error encoding find app query: %s", err) - return stats, err - } - - res, err := project.Es.Search( - project.Es.Search.WithContext(ctx), - project.Es.Search.WithIndex(strings.ToLower(GetESIndexPrefix(nameKey))), - project.Es.Search.WithBody(&buf), - project.Es.Search.WithTrackTotalHits(true), - ) - if err != nil { - log.Printf("[ERROR] Error getting response from Opensearch (find app by name): %s", err) - return stats, err - } - - defer res.Body.Close() - if res.StatusCode == 404 { - return stats, nil - } - - defer res.Body.Close() - if res.IsError() { - var e map[string]interface{} - if err := json.NewDecoder(res.Body).Decode(&e); err != nil { - log.Printf("[WARNING] Error parsing the response body: %s", err) - return stats, err - } - } - - if res.StatusCode != 200 && res.StatusCode != 201 { - return stats, errors.New(fmt.Sprintf("Bad statuscode: %d", res.StatusCode)) - } - - respBody, err := ioutil.ReadAll(res.Body) - - var wrapped OpsDashboardStatSearchWrapper - - // put into struct - err = json.Unmarshal(respBody, &wrapped) - - stats = []OpsDashboardStats{} - for _, hit := range wrapped.Hits.Hits { - stats = append(stats, hit.Source) - } - - if err != nil { - log.Printf("RespBody is: %s", respBody) - return stats, err - } - - log.Printf("[INFO] Successfully got ops dashboard stats") - - } else { - q := datastore.NewQuery(nameKey).Order("-Timestamp").Limit(limit) - _, err := project.Dbclient.GetAll(ctx, q, &stats) - if err != nil { - log.Printf("[WARNING] Failed getting stats for ops dashboard: %s", err) - return stats, err - } - } - - return stats, nil -} - -func SetOpsDashboardCacheHitStat(ctx context.Context, cacheHit bool) error { - nameKey := "opsdashboardstats" - stat := OpsDashboardStats{} - stat.Timestamp = time.Now().Unix() - stat.CacheHit = cacheHit - - data, err := json.Marshal(stat) - - if project.DbType == "opensearch" { - err = indexEs(ctx, nameKey, fmt.Sprintf("%d", stat.Timestamp), data) - if err != nil { - return err - } else { - log.Printf("[INFO] Successfully updated ops dashboard stats") - } - } else { - k := datastore.NameKey(nameKey, fmt.Sprintf("%d", stat.Timestamp), nil) - if _, err := project.Dbclient.Put(ctx, k, &stat); err != nil { - log.Printf("[WARNING] Error updating ops dashboard stats: %s", err) - return err - } - } - - return nil - -} - func SetUser(ctx context.Context, user *User, updateOrg bool) error { log.Printf("[INFO] Updating a user (%s) that has the role %s with %d apps and %d orgs. Org updater: %t", user.Username, user.Role, len(user.PrivateApps), len(user.Orgs), updateOrg) parsedKey := user.Id @@ -5341,6 +5288,91 @@ func SetNewValue(ctx context.Context, newvalue NewValue) error { return nil } +func GetPlatformHealth(ctx context.Context, limit int) ([]HealthCheckDB, error) { + nameKey := "platform_health" + + // sort by "updated", and get the first one + health := []HealthCheckDB{} + + if project.DbType == "opensearch" { + var buf bytes.Buffer + query := map[string]interface{}{ + "sort": map[string]interface{}{ + "updated": map[string]interface{}{ + "order": "desc", + }, + }, + "size": limit, + } + + if err := json.NewEncoder(&buf).Encode(query); err != nil { + log.Printf("[WARNING] Error encoding find user query: %s", err) + return health, err + } + + res, err := project.Es.Search( + project.Es.Search.WithContext(ctx), + project.Es.Search.WithIndex(strings.ToLower(GetESIndexPrefix(nameKey))), + project.Es.Search.WithBody(&buf), + project.Es.Search.WithTrackTotalHits(true), + ) + if err != nil { + log.Printf("[ERROR] Error getting response from Opensearch (get latest platform health): %s", err) + return health, err + } + defer res.Body.Close() + + if res.StatusCode != 200 && res.StatusCode != 201 { + return health, errors.New(fmt.Sprintf("Bad statuscode: %d", res.StatusCode)) + } + + if res.IsError() { + var e map[string]interface{} + if err := json.NewDecoder(res.Body).Decode(&e); err != nil { + log.Printf("[WARNING] Error parsing the response body: %s", err) + return health, err + } else { + // Print the response status and error information. + log.Printf("[%s] %s: %s", + res.Status(), + e["error"].(map[string]interface{})["type"], + e["error"].(map[string]interface{})["reason"], + ) + } + } + + respBody, err := ioutil.ReadAll(res.Body) + if err != nil { + return health, err + } + + wrapped := HealthCheckSearchWrapper{} + err = json.Unmarshal(respBody, &wrapped) + if err != nil { + return health, err + } + + if len(wrapped.Hits.Hits) == 0 { + return health, errors.New("No healthchecks found") + } + + for _, hit := range wrapped.Hits.Hits { + health = append(health, hit.Source) + } + + } else { + q := datastore.NewQuery(nameKey).Order("-updated").Limit(limit) + + _, err := project.Dbclient.GetAll(ctx, q, &health) + if err != nil { + log.Printf("[WARNING] Error getting latest platform health: %s", err) + return health, err + } + } + + return health, nil +} + func SetPlatformHealth(ctx context.Context, health HealthCheckDB) error { nameKey := "platform_health" diff --git a/health.go b/health.go index 373c4ea3..65335c23 100644 --- a/health.go +++ b/health.go @@ -61,7 +61,7 @@ func base64StringToString(base64String string) (string, error) { return string(decoded), nil } -func RunOpsAppHealthCheck() (AppHealth, error) { +func RunOpsAppHealthCheck(apiKey string, orgId string) (AppHealth, error) { log.Printf("[DEBUG] Running app health check") appHealth := AppHealth{ Create: false, @@ -145,7 +145,7 @@ func RunOpsAppHealthCheck() (AppHealth, error) { // set the headers req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) + req.Header.Set("Authorization", "Bearer " + apiKey) // send the request client = &http.Client{} @@ -203,7 +203,7 @@ func RunOpsAppHealthCheck() (AppHealth, error) { // set the headers req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) + req.Header.Set("Authorization", "Bearer "+ apiKey) // send the request client = &http.Client{} @@ -249,7 +249,7 @@ func RunOpsAppHealthCheck() (AppHealth, error) { } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) + req.Header.Set("Authorization", "Bearer "+ apiKey) // send the request client = &http.Client{} @@ -284,7 +284,7 @@ func RunOpsAppHealthCheck() (AppHealth, error) { executeBody.Parameters = []WorkflowAppActionParameter{ { Name: "apikey", - Value: os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY"), + Value: apiKey, }, { Name: "url", @@ -307,7 +307,7 @@ func RunOpsAppHealthCheck() (AppHealth, error) { // set the headers req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) + req.Header.Set("Authorization", "Bearer "+ apiKey) // send the request client = &http.Client{} @@ -365,7 +365,7 @@ func RunOpsAppHealthCheck() (AppHealth, error) { // set the headers req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) + req.Header.Set("Authorization", "Bearer " + apiKey) // send the request client = &http.Client{} @@ -393,18 +393,33 @@ func RunOpsHealthCheck(resp http.ResponseWriter, request *http.Request) { if cors { return } - // Check cache if health check was run in last 5 minutes - // If yes, return cached result, else run health check + + // check if there is a force parameter + force := request.URL.Query().Get("force") + ctx := GetContext(request) platformHealth := HealthCheck{} cacheKey := fmt.Sprintf("ops-health-check") - cacheHit := false - - memcacheUrl := os.Getenv("SHUFFLE_MEMCACHED") apiKey := os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY") orgId := os.Getenv("SHUFFLE_OPS_DASHBOARD_ORG") + if project.Environment == "onprem" && (len(apiKey) == 0 || len(orgId) == 0) { + log.Printf("[DEBUG] Ops dashboard api key or org not set. Getting first org and user") + org, err := GetFirstOrg(ctx) + if err != nil { + log.Printf("[ERROR] Failed getting first org: %s", err) + resp.WriteHeader(500) + resp.Write([]byte(`{"success": false, "reason": "Set up a user and org first!")}`)) + return + } + + log.Printf("[DEBUG] Setting api key to that of user %s and org id to %s ", org.Users[0].ApiKey, org.Id) + + orgId = org.Id + apiKey = org.Users[0].ApiKey + } + if len(apiKey) == 0 || len(orgId) == 0 { log.Printf("[WARNING] Ops dashboard api key or org not set. Not setting up ops workflow") resp.WriteHeader(500) @@ -412,23 +427,15 @@ func RunOpsHealthCheck(resp http.ResponseWriter, request *http.Request) { return } - if len(memcacheUrl) != 0 { + if project.CacheDb && force != "true" { cache, err := GetCache(ctx, cacheKey) if err == nil { cacheData := []byte(cache.([]uint8)) //log.Printf("CACHEDATA: %s", cacheData) err = json.Unmarshal(cacheData, &platformHealth) if err == nil { - // FIXME: Check if last updated is less than 5 minutes with platformHealth.Edited in unix time - // If yes, return cached result, else run health check log.Printf("Platform health returned: %#v", platformHealth) marshalledData, err := json.Marshal(platformHealth) - cacheHit = true - - err_ := SetOpsDashboardCacheHitStat(ctx, cacheHit) - if err_ != nil { - log.Printf("[WARNING] Failed setting cache hit stat: %s", err_) - } if err == nil { resp.WriteHeader(200) @@ -441,22 +448,79 @@ func RunOpsHealthCheck(resp http.ResponseWriter, request *http.Request) { } else { log.Printf("[WARNING] Failed getting cache ops health on first try: %s", err) } - } else { - log.Println("[WARNING] Memcache URL not set! Exiting..") + } else if !(project.CacheDb) { + log.Println("[WARNING] Cache not enabled. Not using cache for ops health isn't recommended!") resp.WriteHeader(500) - resp.Write([]byte(`{"success": false, "reason": "SHUFFLE_MEMCACHED not set. Please set memcached to use this feature!"}`)) + resp.Write([]byte(`{"success": false, "reason": "Cache not enabled. Not using cache for ops health isn't recommended!"}`)) return } + if force == "true" { + log.Printf("[DEBUG] Force is true. Running health check") + + userInfo, err := HandleApiAuthentication(resp, request) + if err != nil { + log.Printf("[WARNING] Api authentication failed in handleInfo: %s", err) + + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if project.Environment == "onprem" && userInfo.Role != "admin" { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Only admins can run health check!"}`)) + return + } else if project.Environment == "Cloud" && userInfo.ApiKey != os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY") { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": "Only admins can run health check!"}`)) + return + } + } else if force != "true" { + // get last health check from database + healths, err := GetPlatformHealth(ctx, 1) + + health := healths[0] + + if err == nil { + log.Printf("[DEBUG] Last health check was: %#v", health) + platformData, err := json.Marshal(health) + if err != nil { + log.Printf("[ERROR] Failed marshalling platform health data: %s", err) + resp.WriteHeader(500) + resp.Write([]byte(`{"success": false, "reason": "Failed JSON parsing platform health."}`)) + return + } + + resp.WriteHeader(200) + resp.Write(platformData) + return + } else { + log.Printf("[WARNING] Failed getting platform health from database: %s", err) + resp.WriteHeader(500) + resp.Write([]byte(`{"success": false, "reason": "Failed getting platform health from database."}`)) + return + } + } + // Use channel for getting RunOpsWorkflow function results workflowHealthChannel := make(chan WorkflowHealth) // appHealthChannel := make(chan AppHealth) go func() { - workflowHealth, err := RunOpsWorkflow() + workflowHealth, err := RunOpsWorkflow(apiKey, orgId) if err != nil { - log.Printf("[ERROR] Failed running workflow health check: %s", err) - workflowHealthChannel <- workflowHealth - return + log.Printf("[ERROR] Failed workflow health check: %s", err) + } + if workflowHealth.Create == true { + log.Printf("[DEBUG] Deleting created ops workflow") + err = deleteWorkflow(workflowHealth, apiKey) + if err != nil { + log.Printf("[ERROR] Failed deleting workflow: %s", err) + } else { + log.Printf("[DEBUG] Deleted ops workflow successfully!") + workflowHealth.Delete = true + updateCache(workflowHealth) + } } workflowHealthChannel <- workflowHealth }() @@ -506,31 +570,79 @@ func RunOpsHealthCheck(resp http.ResponseWriter, request *http.Request) { resp.Write(platformData) } -func OpsDashboardCacheHitStat(resp http.ResponseWriter, request *http.Request) { +func GetOpsDashboardStats(resp http.ResponseWriter, request *http.Request) { + // for now, the limit is last 100 runs + limit := 100 + + healthChecks := []HealthCheckDB{} ctx := GetContext(request) - stats, err := GetOpsDashboardCacheHitStat(ctx, 30) + healthChecks, err := GetPlatformHealth(ctx, limit) if err != nil { - log.Printf("[ERROR] Failed getting cache hit stats: %s", err) + log.Printf("[ERROR] Failed getting platform health from database: %s", err) resp.WriteHeader(500) - resp.Write([]byte(`{"success": false, "reason": "Failed getting cache hit stats. Contact support@shuffler.io"}`)) + resp.Write([]byte(`{"success": false, "reason": "Failed getting platform health from database."}`)) return - } - - log.Printf("Stats are: %#v", stats) + } - statsBody, err := json.Marshal(stats) + healthChecksData, err := json.Marshal(healthChecks) if err != nil { - log.Printf("[ERROR] Failed marshalling cache hit stats: %s", err) + log.Printf("[ERROR] Failed marshalling platform health data: %s", err) resp.WriteHeader(500) - resp.Write([]byte(`{"success": false, "reason": "Failed JSON parsing cache hit stats. Contact support@shuffler.io"}`)) + resp.Write([]byte(`{"success": false, "reason": "Failed JSON parsing platform health."}`)) + return } resp.WriteHeader(200) - resp.Write(statsBody) + resp.Write(healthChecksData) +} + +func deleteWorkflow(workflowHealth WorkflowHealth , apiKey string) (error) { + baseUrl := os.Getenv("SHUFFLE_CLOUDRUN_URL") + if len(baseUrl) == 0 { + baseUrl = "https://shuffler.io" + } + + if project.Environment == "onprem" { + baseUrl = "http://localhost:5001" + } + + id := workflowHealth.ExecutionId + + // 4. Delete workflow + // make a DELETE request to https://shuffler.io/api/v1/workflows/ + url := baseUrl + "/api/v1/workflows/" + id + log.Printf("[DEBUG] Deleting workflow with id: %s", id) + + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + log.Printf("[ERROR] Failed creating HTTP request: %s", err) + return err + } + + // set the headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+apiKey) + + // send the request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Printf("[ERROR] Failed deleting the health check workflow with HTTP request: %s", err) + return err + } + + if resp.StatusCode != 200 { + log.Printf("[ERROR] Failed deleting the health check workflow: %s. The status code was: %d", err, resp.StatusCode) + return err + } + + defer resp.Body.Close() + + return nil } -func RunOpsWorkflow() (WorkflowHealth, error) { +func RunOpsWorkflow(apiKey string, orgId string) (WorkflowHealth, error) { // run workflow with id 602c7cf5-500e-4bd1-8a97-aa5bc8a554e6 ctx := context.Background() @@ -543,8 +655,17 @@ func RunOpsWorkflow() (WorkflowHealth, error) { ExecutionId: "", } + baseUrl := os.Getenv("SHUFFLE_CLOUDRUN_URL") + if len(baseUrl) == 0 { + baseUrl = "https://shuffler.io" + } + + if project.Environment == "onprem" { + baseUrl = "http://localhost:5001" + } + // 1. Get workflow - opsWorkflowID, err := InitOpsWorkflow() + opsWorkflowID, err := InitOpsWorkflow(apiKey, orgId) if err != nil { log.Printf("[ERROR] Failed creating Health check workflow: %s", err) return workflowHealth, err @@ -566,19 +687,9 @@ func RunOpsWorkflow() (WorkflowHealth, error) { // 2. Run workflow id := workflow.ID - orgId := os.Getenv("SHUFFLE_OPS_DASHBOARD_ORG") _ = id _ = orgId - baseUrl := os.Getenv("SHUFFLE_CLOUDRUN_URL") - if len(baseUrl) == 0 { - baseUrl = "https://shuffler.io" - } - - if project.Environment == "onprem" { - baseUrl = "http://localhost:5001" - } - // prepare the request url := baseUrl + "/api/v1/workflows/" + id + "/execute" log.Printf("[DEBUG] Running health check workflow with URL: %s", url) @@ -590,7 +701,7 @@ func RunOpsWorkflow() (WorkflowHealth, error) { // set the headers req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) + req.Header.Set("Authorization", "Bearer "+ apiKey) // startId := "98713d6a-dd6b-4bd6-a11c-9778b80f2a28" // body := map[string]string{"execution_argument": "", "start": startId} @@ -656,7 +767,7 @@ func RunOpsWorkflow() (WorkflowHealth, error) { // set the headers req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) + req.Header.Set("Authorization", "Bearer " + apiKey) // convert the body to JSON reqBody := map[string]string{"execution_id": execution.ExecutionId, "authorization": os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")} @@ -707,50 +818,20 @@ func RunOpsWorkflow() (WorkflowHealth, error) { time.Sleep(2 * time.Second) } - // 4. Delete workflow - // make a DELETE request to https://shuffler.io/api/v1/workflows/ - url = baseUrl + "/api/v1/workflows/" + id - log.Printf("[DEBUG] Deleting workflow with id: %s", id) - - req, err = http.NewRequest("DELETE", url, nil) - if err != nil { - log.Printf("[ERROR] Failed creating HTTP request: %s", err) - return workflowHealth, err - } - - // set the headers - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY")) - - // send the request - client = &http.Client{} - resp, err = client.Do(req) - if err != nil { - log.Printf("[ERROR] Failed deleting the health check workflow with HTTP request: %s", err) - return workflowHealth, err - } - - if resp.StatusCode != 200 { - log.Printf("[ERROR] Failed deleting the health check workflow: %s. The status code was: %d", err, resp.StatusCode) - return workflowHealth, err - } - - defer resp.Body.Close() - - workflowHealth.Delete = true - return workflowHealth, nil } -func InitOpsWorkflow() (string, error) { - opsDashboardApikey := os.Getenv("SHUFFLE_OPS_DASHBOARD_APIKEY") +func InitOpsWorkflow(apiKey string, OrgId string) (string, error) { + opsDashboardApikey := apiKey + opsDashboardOrgId := OrgId + if len(opsDashboardApikey) == 0 { log.Printf("[WARNING] Ops dashboard api key not set. Not setting up ops workflow") return "", errors.New("Ops dashboard api key not set") } - opsDashboardOrgId := os.Getenv("SHUFFLE_OPS_DASHBOARD_ORG") + if len(opsDashboardOrgId) == 0 { log.Printf("[WARNING] Ops dashboard org not set. Not setting up ops workflow") return "", errors.New("Ops dashboard org not set") @@ -830,6 +911,7 @@ func InitOpsWorkflow() (string, error) { workflowData.Public = false workflowData.Status = "" workflowData.Name = "Ops Dashboard Workflow" + workflowData.Hidden = true var actions []Action // var blacklisted = []string{"Date_to_epoch", "input_data", "Compare_timestamps", "Get_current_timestamp"} diff --git a/structs.go b/structs.go index 778c8e90..13ed3e02 100755 --- a/structs.go +++ b/structs.go @@ -1044,11 +1044,6 @@ type Comment struct { } `json:"position"` } -type OpsDashboardStats struct { - Timestamp int64 `json:"timestamp" datastore:"timestamp"` - CacheHit bool `json:"cachehit" datastore:"cachehit"` -} - type Workflow struct { Actions []Action `json:"actions" datastore:"actions,noindex"` Branches []Branch `json:"branches" datastore:"branches,noindex"` @@ -1576,6 +1571,31 @@ type StatisticsItem struct { OrgId string `json:"org_id" datastore:"org_id"` } +type HealthCheckSearchWrapper struct { + Took int `json:"took"` + TimedOut bool `json:"timed_out"` + Shards struct { + Total int `json:"total"` + Successful int `json:"successful"` + Skipped int `json:"skipped"` + Failed int `json:"failed"` + } `json:"_shards"` + Hits struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + MaxScore float64 `json:"max_score"` + Hits []struct { + Index string `json:"_index"` + Type string `json:"_type"` + ID string `json:"_id"` + Score float64 `json:"_score"` + Source HealthCheckDB `json:"_source"` + } `json:"hits"` + } `json:"hits"` +} + type NewValueSearchWrapper struct { Took int `json:"took"` TimedOut bool `json:"timed_out"` @@ -1876,31 +1896,6 @@ type ExecRequestSearchWrapper struct { } `json:"hits"` } -type OpsDashboardStatSearchWrapper struct { - Took int `json:"took"` - TimedOut bool `json:"timed_out"` - Shards struct { - Total int `json:"total"` - Successful int `json:"successful"` - Skipped int `json:"skipped"` - Failed int `json:"failed"` - } `json:"_shards"` - Hits struct { - Total struct { - Value int `json:"value"` - Relation string `json:"relation"` - } `json:"total"` - MaxScore float64 `json:"max_score"` - Hits []struct { - Index string `json:"_index"` - Type string `json:"_type"` - ID string `json:"_id"` - Score float64 `json:"_score"` - Source OpsDashboardStats `json:"_source"` - } `json:"hits"` - } `json:"hits"` -} - type AppAuthSearchWrapper struct { Took int `json:"took"` TimedOut bool `json:"timed_out"`