From 72557fd0bf28e0c5b456c7030542b66e55f0da2d Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Thu, 2 May 2024 16:32:01 +0200 Subject: [PATCH 01/13] feat: add statistics series render to job view metric plots --- web/frontend/src/Job.root.svelte | 3 --- web/frontend/src/Metric.svelte | 37 ++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/web/frontend/src/Job.root.svelte b/web/frontend/src/Job.root.svelte index 8d090ac..00f64e2 100644 --- a/web/frontend/src/Job.root.svelte +++ b/web/frontend/src/Job.root.svelte @@ -306,9 +306,6 @@ {/if} - diff --git a/web/frontend/src/Metric.svelte b/web/frontend/src/Metric.svelte index 6022ffb..a3bedaa 100644 --- a/web/frontend/src/Metric.svelte +++ b/web/frontend/src/Metric.svelte @@ -33,8 +33,17 @@ error = null; let selectedScope = minScope(scopes); + let statsPattern = /(.*)-stats$/ + let statsSeries = rawData.map((data) => data?.statisticsSeries ? data.statisticsSeries : null) + let selectedScopeIndex + $: availableScopes = scopes; - $: selectedScopeIndex = scopes.findIndex((s) => s == selectedScope); + $: patternMatches = statsPattern.exec(selectedScope) + $: if (!patternMatches) { + selectedScopeIndex = scopes.findIndex((s) => s == selectedScope); + } else { + selectedScopeIndex = scopes.findIndex((s) => s == patternMatches[1]); + } $: data = rawData[selectedScopeIndex]; $: series = data?.series.filter( (series) => selectedHost == null || series.hostname == selectedHost, @@ -62,6 +71,7 @@ if (jm.scope != "node") { scopes = [...scopes, jm.scope]; rawData.push(jm.metric); + statsSeries = rawData.map((data) => data?.statisticsSeries ? data.statisticsSeries : null) selectedScope = jm.scope; selectedScopeIndex = scopes.findIndex((s) => s == jm.scope); dispatch("more-loaded", jm); @@ -79,15 +89,18 @@ : "") + (metricConfig?.unit?.base ? metricConfig.unit.base : "")}) {#if job.resources.length > 1} - {#each job.resources as { hostname }} @@ -100,7 +113,7 @@ {:else if error != null} {error.message} - {:else if series != null} + {:else if series != null && !patternMatches} + {:else if statsSeries[selectedScopeIndex] != null && patternMatches} + {/if} {/key} From 597bccc080ad17478a14cb72768655573a80fa62 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Mon, 6 May 2024 13:15:15 +0200 Subject: [PATCH 02/13] fix: add SQL JSON validity check to meta_data query --- internal/repository/query.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/repository/query.go b/internal/repository/query.go index 94aa742..5ca98fb 100644 --- a/internal/repository/query.go +++ b/internal/repository/query.go @@ -236,6 +236,9 @@ func buildStringCondition(field string, cond *model.StringInput, query sq.Select } func buildMetaJsonCondition(jsonField string, cond *model.StringInput, query sq.SelectBuilder) sq.SelectBuilder { + // Verify and Search Only in Valid Jsons + query = query.Where("JSON_VALID(meta_data)") + // add "AND" Sql query Block for field match if cond.Eq != nil { return query.Where("JSON_EXTRACT(meta_data, \"$."+jsonField+"\") = ?", *cond.Eq) } From 54f7980162c1f3321e223ebe88477df6529cce28 Mon Sep 17 00:00:00 2001 From: Jan Eitzinger Date: Thu, 16 May 2024 11:18:57 +0200 Subject: [PATCH 03/13] fix: Add required key to init config file --- cmd/cc-backend/main.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index 9d084f2..ed471ac 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -76,6 +76,9 @@ const configString = ` "kind": "file", "path": "./var/job-archive" }, + "jwts": { + "max-age": "2000h" + }, "clusters": [ { "name": "name", @@ -115,15 +118,15 @@ func initEnv() { os.Exit(0) } - if err := os.WriteFile("config.json", []byte(configString), 0666); err != nil { + if err := os.WriteFile("config.json", []byte(configString), 0o666); err != nil { log.Fatalf("Writing config.json failed: %s", err.Error()) } - if err := os.WriteFile(".env", []byte(envString), 0666); err != nil { + if err := os.WriteFile(".env", []byte(envString), 0o666); err != nil { log.Fatalf("Writing .env failed: %s", err.Error()) } - if err := os.Mkdir("var", 0777); err != nil { + if err := os.Mkdir("var", 0o777); err != nil { log.Fatalf("Mkdir var failed: %s", err.Error()) } From b48d1b8ad6904d97e80f9f4753ba0a22240a4225 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 22 May 2024 14:21:54 +0200 Subject: [PATCH 04/13] fix: correct status view columns on mobile displays --- web/frontend/src/Status.root.svelte | 40 +++++++++++++++-------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/web/frontend/src/Status.root.svelte b/web/frontend/src/Status.root.svelte index 4121ead..132cb2e 100644 --- a/web/frontend/src/Status.root.svelte +++ b/web/frontend/src/Status.root.svelte @@ -315,20 +315,11 @@ - - + +

Current utilization of cluster "{cluster}"

- - {#if $initq.fetching || $mainQuery.fetching} - - {:else if $initq.error} - {$initq.error.message} - {:else} - - {/if} - - + - + { @@ -347,6 +338,17 @@ />
+ + + {#if $initq.fetching || $mainQuery.fetching} + + {:else if $initq.error} + {$initq.error.message} + {:else} + + {/if} + + {#if $mainQuery.error} @@ -361,8 +363,8 @@ {#if $initq.data && $mainQuery.data} {#each $initq.data.clusters.find((c) => c.name == cluster).subClusters as subCluster, i} - - + + SubCluster "{subCluster.name}" @@ -433,7 +435,7 @@ - +
{#key $mainQuery.data.nodeMetrics} - +

@@ -580,7 +582,7 @@
- +
{#key $mainQuery.data.stats} @@ -610,7 +612,7 @@ {/key} - +
{#key $mainQuery.data.stats} From 061c9f0979d53a873a0acc9c07f405193b6b7997 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 22 May 2024 15:57:22 +0200 Subject: [PATCH 05/13] fix: deselected metrics were marked as missing on new jobview load --- web/frontend/src/Job.root.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/frontend/src/Job.root.svelte b/web/frontend/src/Job.root.svelte index 00f64e2..8cf8f87 100644 --- a/web/frontend/src/Job.root.svelte +++ b/web/frontend/src/Job.root.svelte @@ -165,10 +165,11 @@ .find((c) => c.name == job.cluster) .metricConfig.map((mc) => mc.name); - // Metric not found in JobMetrics && Metric not explicitly disabled: Was expected, but is Missing + // Metric not found in JobMetrics && Metric not explicitly disabled in config or deselected: Was expected, but is Missing missingMetrics = metricNames.filter( (metric) => !metrics.some((jm) => jm.name == metric) && + selectedMetrics.includes(metric) && !checkMetricDisabled( metric, $initq.data.job.cluster, From 575753038b981ce093e6852cc8762ecb78319be6 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 22 May 2024 18:22:35 +0200 Subject: [PATCH 06/13] feat: add jobname filter to joblist textfilter - allows combination of filters now including jobname - rename component --- web/frontend/src/Jobs.root.svelte | 4 +- ...UserOrProject.svelte => TextFilter.svelte} | 74 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) rename web/frontend/src/filters/{UserOrProject.svelte => TextFilter.svelte} (52%) diff --git a/web/frontend/src/Jobs.root.svelte b/web/frontend/src/Jobs.root.svelte index 204a4e3..fb49b39 100644 --- a/web/frontend/src/Jobs.root.svelte +++ b/web/frontend/src/Jobs.root.svelte @@ -14,7 +14,7 @@ import Refresher from "./joblist/Refresher.svelte"; import Sorting from "./joblist/SortSelection.svelte"; import MetricSelection from "./MetricSelection.svelte"; - import UserOrProject from "./filters/UserOrProject.svelte"; + import TextFilter from "./filters/TextFilter.svelte"; const { query: initq } = init(); @@ -86,7 +86,7 @@ - filterComponent.update(detail)} diff --git a/web/frontend/src/filters/UserOrProject.svelte b/web/frontend/src/filters/TextFilter.svelte similarity index 52% rename from web/frontend/src/filters/UserOrProject.svelte rename to web/frontend/src/filters/TextFilter.svelte index 983192c..80f454a 100644 --- a/web/frontend/src/filters/UserOrProject.svelte +++ b/web/frontend/src/filters/TextFilter.svelte @@ -4,21 +4,25 @@ const dispatch = createEventDispatcher(); - export let user = ""; - export let project = ""; export let authlevel; export let roles; - let mode = "user", - term = ""; + let mode = "user"; + let term = ""; + let user = ""; + let project = ""; + let jobName = ""; const throttle = 500; function modeChanged() { if (mode == "user") { project = term; term = user; - } else { + } else if (mode == "project") { user = term; term = project; + } else { + jobName = term; + term = jobName; } termChanged(0); } @@ -28,7 +32,8 @@ function termChanged(sleep = throttle) { if (authlevel >= roles.manager) { if (mode == "user") user = term; - else project = term; + else if (mode == "project") project = term; + else jobName = term; if (timeoutId != null) clearTimeout(timeoutId); @@ -36,49 +41,44 @@ dispatch("update", { user, project, + jobName }); }, sleep); } else { - project = term; + if (mode == "project") project = term; + else jobName = term; + if (timeoutId != null) clearTimeout(timeoutId); timeoutId = setTimeout(() => { dispatch("update", { project, + jobName }); }, sleep); } } -{#if authlevel >= roles.manager} - - + {#if authlevel >= roles.manager} - - - termChanged()} - on:keyup={(event) => termChanged(event.key == "Enter" ? 0 : throttle)} - placeholder={mode == "user" ? "filter username..." : "filter project..."} - /> - -{:else} - - - termChanged()} - on:keyup={(event) => termChanged(event.key == "Enter" ? 0 : throttle)} - placeholder="filter project..." - /> - -{/if} + {/if} + + + + termChanged()} + on:keyup={(event) => termChanged(event.key == "Enter" ? 0 : throttle)} + placeholder={`filter ${mode}...`} + /> + + From ba1658beac1094b4bb387d10fd4a85450ddc6574 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 22 May 2024 18:50:52 +0200 Subject: [PATCH 07/13] fix: correct selectable histogram placement in status view --- web/frontend/src/Status.root.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/frontend/src/Status.root.svelte b/web/frontend/src/Status.root.svelte index 132cb2e..48c3711 100644 --- a/web/frontend/src/Status.root.svelte +++ b/web/frontend/src/Status.root.svelte @@ -644,7 +644,7 @@
{#if metricsInHistograms} - + {#key $mainQuery.data.stats[0].histMetrics} Date: Thu, 23 May 2024 11:53:23 +0200 Subject: [PATCH 08/13] fix: fix jobname and arrayjobid timeouts by adding additional 30d filter - improve archive worker logs - add arrayjobid filter to url if used --- internal/repository/job.go | 6 +++--- internal/routerConfig/routes.go | 12 ++++++++++-- web/frontend/src/filters/Filters.svelte | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/internal/repository/job.go b/internal/repository/job.go index ce5e416..b42598d 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -520,7 +520,7 @@ func (r *JobRepository) archivingWorker() { // not using meta data, called to load JobMeta into Cache? // will fail if job meta not in repository if _, err := r.FetchMetadata(job); err != nil { - log.Errorf("archiving job (dbid: %d) failed: %s", job.ID, err.Error()) + log.Errorf("archiving job (dbid: %d) failed at check metadata step: %s", job.ID, err.Error()) r.UpdateMonitoringStatus(job.ID, schema.MonitoringStatusArchivingFailed) continue } @@ -529,14 +529,14 @@ func (r *JobRepository) archivingWorker() { // TODO: Maybe use context with cancel/timeout here jobMeta, err := metricdata.ArchiveJob(job, context.Background()) if err != nil { - log.Errorf("archiving job (dbid: %d) failed: %s", job.ID, err.Error()) + log.Errorf("archiving job (dbid: %d) failed at archiving job step: %s", job.ID, err.Error()) r.UpdateMonitoringStatus(job.ID, schema.MonitoringStatusArchivingFailed) continue } // Update the jobs database entry one last time: if err := r.MarkArchived(job.ID, schema.MonitoringStatusArchivingSuccessful, jobMeta.Statistics); err != nil { - log.Errorf("archiving job (dbid: %d) failed: %s", job.ID, err.Error()) + log.Errorf("archiving job (dbid: %d) failed at marking archived step: %s", job.ID, err.Error()) continue } log.Debugf("archiving job %d took %s", job.JobID, time.Since(start)) diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index fe374ac..c7a5a0c 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -302,11 +302,19 @@ func HandleSearchBar(rw http.ResponseWriter, r *http.Request, buildInfo web.Buil case "jobId": http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery case "jobName": - http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery + // Add Last 30 Days to migitate timeouts + untilTime := strconv.FormatInt(time.Now().Unix(), 10) + fromTime := strconv.FormatInt((time.Now().Unix() - int64(30*24*3600)), 10) + + http.Redirect(rw, r, "/monitoring/jobs/?startTime="+fromTime+"-"+untilTime+"&jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery case "projectId": http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery case "arrayJobId": - http.Redirect(rw, r, "/monitoring/jobs/?arrayJobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery + // Add Last 30 Days to migitate timeouts + untilTime := strconv.FormatInt(time.Now().Unix(), 10) + fromTime := strconv.FormatInt((time.Now().Unix() - int64(30*24*3600)), 10) + + http.Redirect(rw, r, "/monitoring/jobs/?startTime="+fromTime+"-"+untilTime+"&arrayJobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery case "username": if user.HasAnyRole([]schema.Role{schema.RoleAdmin, schema.RoleSupport, schema.RoleManager}) { http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) diff --git a/web/frontend/src/filters/Filters.svelte b/web/frontend/src/filters/Filters.svelte index 8e7a8ef..a27c799 100644 --- a/web/frontend/src/filters/Filters.svelte +++ b/web/frontend/src/filters/Filters.svelte @@ -193,6 +193,7 @@ opts.push(`userMatch=${filters.userMatch}`); if (filters.project) opts.push(`project=${filters.project}`); if (filters.jobName) opts.push(`jobName=${filters.jobName}`); + if (filters.arrayJobId) opts.push(`arrayJobId=${filters.arrayJobId}`); if (filters.projectMatch != "contains") opts.push(`projectMatch=${filters.projectMatch}`); From 8d1228c9e8b5fa96f29745250e51c13b335ef247 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Thu, 23 May 2024 15:43:09 +0200 Subject: [PATCH 09/13] feat: rework list searchbar, adds project-specific mode, add to user-joblist --- web/frontend/src/Jobs.root.svelte | 2 + web/frontend/src/User.root.svelte | 6 +++ web/frontend/src/filters/Filters.svelte | 2 +- web/frontend/src/filters/TextFilter.svelte | 46 +++++++++++++++------- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/web/frontend/src/Jobs.root.svelte b/web/frontend/src/Jobs.root.svelte index fb49b39..f7c99ff 100644 --- a/web/frontend/src/Jobs.root.svelte +++ b/web/frontend/src/Jobs.root.svelte @@ -38,6 +38,7 @@ ? !!ccconfig[`plot_list_showFootprint:${filterPresets.cluster}`] : !!ccconfig.plot_list_showFootprint; let selectedCluster = filterPresets?.cluster ? filterPresets.cluster : null; + let presetProject = filterPresets?.project ? filterPresets.project : "" // The filterPresets are handled by the Filters component, // so we need to wait for it to be ready before we can start a query. @@ -87,6 +88,7 @@ filterComponent.update(detail)} diff --git a/web/frontend/src/User.root.svelte b/web/frontend/src/User.root.svelte index c60ea20..41969d9 100644 --- a/web/frontend/src/User.root.svelte +++ b/web/frontend/src/User.root.svelte @@ -12,6 +12,7 @@ } from "@sveltestrap/sveltestrap"; import { queryStore, gql, getContextClient } from "@urql/svelte"; import Filters from "./filters/Filters.svelte"; + import TextFilter from "./filters/TextFilter.svelte" import JobList from "./joblist/JobList.svelte"; import Sorting from "./joblist/SortSelection.svelte"; import Refresher from "./joblist/Refresher.svelte"; @@ -132,6 +133,11 @@ /> + filterComponent.update(detail)} + /> + + jobList.refresh()} /> diff --git a/web/frontend/src/filters/Filters.svelte b/web/frontend/src/filters/Filters.svelte index a27c799..7253ff7 100644 --- a/web/frontend/src/filters/Filters.svelte +++ b/web/frontend/src/filters/Filters.svelte @@ -194,7 +194,7 @@ if (filters.project) opts.push(`project=${filters.project}`); if (filters.jobName) opts.push(`jobName=${filters.jobName}`); if (filters.arrayJobId) opts.push(`arrayJobId=${filters.arrayJobId}`); - if (filters.projectMatch != "contains") + if (filters.project && filters.projectMatch != "contains") opts.push(`projectMatch=${filters.projectMatch}`); if (opts.length == 0 && window.location.search.length <= 1) return; diff --git a/web/frontend/src/filters/TextFilter.svelte b/web/frontend/src/filters/TextFilter.svelte index 80f454a..b010175 100644 --- a/web/frontend/src/filters/TextFilter.svelte +++ b/web/frontend/src/filters/TextFilter.svelte @@ -1,28 +1,29 @@ @@ -67,10 +76,12 @@ bind:value={mode} on:change={modeChanged} > - {#if authlevel >= roles.manager} + {#if !presetProject} + + {/if} + {#if roles && authlevel >= roles.manager} {/if} - termChanged()} on:keyup={(event) => termChanged(event.key == "Enter" ? 0 : throttle)} - placeholder={`filter ${mode}...`} + placeholder={presetProject ? `Filter ${mode} in ${presetProject} ...` : `Filter ${mode} ...`} /> + {#if presetProject} + + {/if} From 320c87a1dbc41a15db63dac68c9327f9725cba24 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Mon, 27 May 2024 11:11:25 +0200 Subject: [PATCH 10/13] fix: add additional 30d fitler to searchbar fallback handling --- internal/routerConfig/routes.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index c7a5a0c..1dd6dee 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -347,7 +347,11 @@ func HandleSearchBar(rw http.ResponseWriter, r *http.Request, buildInfo web.Buil } else if project != "" { http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(project), http.StatusFound) // projectId (equal) } else if jobname != "" { - http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(jobname), http.StatusFound) // JobName (contains) + // Add Last 30 Days to migitate timeouts + untilTime := strconv.FormatInt(time.Now().Unix(), 10) + fromTime := strconv.FormatInt((time.Now().Unix() - int64(30*24*3600)), 10) + + http.Redirect(rw, r, "/monitoring/jobs/?startTime="+fromTime+"-"+untilTime+"&jobName="+url.QueryEscape(jobname), http.StatusFound) // 30D Fitler + JobName (contains) } else { web.RenderTemplate(rw, "message.tmpl", &web.Page{Title: "Info", MsgType: "alert-info", Message: "Search without result", User: *user, Roles: availableRoles, Build: buildInfo}) } From a4397d54479ee44b56b80a93703e8bff2617dbca Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Mon, 27 May 2024 12:09:55 +0200 Subject: [PATCH 11/13] fix: add scramble to textfilter component --- web/frontend/src/filters/TextFilter.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/frontend/src/filters/TextFilter.svelte b/web/frontend/src/filters/TextFilter.svelte index b010175..db1f184 100644 --- a/web/frontend/src/filters/TextFilter.svelte +++ b/web/frontend/src/filters/TextFilter.svelte @@ -1,6 +1,7 @@