mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-10-02 20:54:32 +02:00
Add ui config tests and fix bugs
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// All rights reserved. This file is part of cc-backend.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
|
@@ -26,7 +26,7 @@ var (
|
||||
type UserCfgRepo struct {
|
||||
DB *sqlx.DB
|
||||
Lookup *sqlx.Stmt
|
||||
uiDefaults web.WebConfig
|
||||
uiDefaults map[string]any
|
||||
cache *lrucache.Cache
|
||||
lock sync.RWMutex
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func GetUserCfgRepo() *UserCfgRepo {
|
||||
userCfgRepoInstance = &UserCfgRepo{
|
||||
DB: db.DB,
|
||||
Lookup: lookupConfigStmt,
|
||||
uiDefaults: web.UIDefaults,
|
||||
uiDefaults: web.UIDefaultsMap,
|
||||
cache: lrucache.New(1024),
|
||||
}
|
||||
})
|
||||
@@ -55,13 +55,12 @@ func GetUserCfgRepo() *UserCfgRepo {
|
||||
// user or return the plain default config.
|
||||
func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]any, error) {
|
||||
if user == nil {
|
||||
uCfg.lock.RLock()
|
||||
copy := make(map[string]any, len(uCfg.uiDefaults))
|
||||
maps.Copy(copy, uCfg.uiDefaults)
|
||||
uCfg.lock.RUnlock()
|
||||
return copy, nil
|
||||
}
|
||||
|
||||
// Is the cache invalidated in case the options are changed?
|
||||
data := uCfg.cache.Get(user.Username, func() (any, time.Duration, int) {
|
||||
uiconfig := make(map[string]any, len(uCfg.uiDefaults))
|
||||
maps.Copy(uiconfig, uCfg.uiDefaults)
|
||||
@@ -113,15 +112,6 @@ func (uCfg *UserCfgRepo) UpdateConfig(
|
||||
user *schema.User,
|
||||
) error {
|
||||
if user == nil {
|
||||
var val any
|
||||
if err := json.Unmarshal([]byte(value), &val); err != nil {
|
||||
cclog.Warn("Error while unmarshaling raw user config json")
|
||||
return err
|
||||
}
|
||||
|
||||
uCfg.lock.Lock()
|
||||
defer uCfg.lock.Unlock()
|
||||
uCfg.uiDefaults[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -5,8 +5,7 @@
|
||||
|
||||
package web
|
||||
|
||||
var configSchema = `
|
||||
{
|
||||
const configSchema = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jobList": {
|
||||
@@ -21,8 +20,7 @@ var configSchema = `
|
||||
"description": "If footprint bars are shown as first column by default.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["usePaging", "showFootprint"]
|
||||
}
|
||||
},
|
||||
"nodeList": {
|
||||
"description": "Node list defaults. Applies to node list view.",
|
||||
@@ -32,8 +30,7 @@ var configSchema = `
|
||||
"description": "If classic paging is used instead of continuous scrolling by default.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["usePaging"]
|
||||
}
|
||||
},
|
||||
"jobView": {
|
||||
"description": "Job view defaults.",
|
||||
@@ -55,8 +52,7 @@ var configSchema = `
|
||||
"description": "If the job metric statistics table is shown by default.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showFootprint"]
|
||||
}
|
||||
},
|
||||
"metricConfig": {
|
||||
"description": "Global initial metric selections for primary views of all clusters.",
|
||||
@@ -95,9 +91,33 @@ var configSchema = `
|
||||
"name": {
|
||||
"description": "The name of the cluster."
|
||||
},
|
||||
"subClusters" {
|
||||
"jobListMetrics": {
|
||||
"description": "Initial metrics shown for new users in job lists (User and jobs view) for subcluster.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"jobViewPlotMetrics": {
|
||||
"description": "Initial metrics shown for new users as job view timeplots for subcluster.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"jobViewTableMetrics": {
|
||||
"description": "Initial metrics shown for new users in job view statistics table for subcluster.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"subClusters": {
|
||||
"description": "The array of overrides per subcluster.",
|
||||
"type":"array",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -138,8 +158,7 @@ var configSchema = `
|
||||
"required": ["name", "subClusters"],
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"required": ["jobListMetrics", "jobViewPlotMetrics", "jobViewTableMetrics"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"plotConfiguration": {
|
||||
@@ -165,7 +184,7 @@ var configSchema = `
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["colorBackground", "plotsPerRow", "lineWidth"]
|
||||
}
|
||||
}
|
||||
}`
|
||||
}
|
||||
}`
|
||||
|
57
web/web.go
57
web/web.go
@@ -13,7 +13,6 @@ import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ClusterCockpit/cc-backend/internal/config"
|
||||
"github.com/ClusterCockpit/cc-backend/pkg/archive"
|
||||
@@ -75,8 +74,6 @@ type PlotConfiguration struct {
|
||||
ColorScheme []string `json:"colorScheme"`
|
||||
}
|
||||
|
||||
var initOnce sync.Once
|
||||
|
||||
var UIDefaults = WebConfig{
|
||||
JobList: JobListConfig{
|
||||
UsePaging: false,
|
||||
@@ -104,6 +101,8 @@ var UIDefaults = WebConfig{
|
||||
},
|
||||
}
|
||||
|
||||
var UIDefaultsMap map[string]any
|
||||
|
||||
//
|
||||
// map[string]any{
|
||||
// "analysis_view_histogramMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
||||
@@ -117,16 +116,60 @@ var UIDefaults = WebConfig{
|
||||
// "status_view_selectedTopProjectCategory": "totalJobs",
|
||||
// }
|
||||
|
||||
func Init(rawConfig json.RawMessage, disableArchive bool) error {
|
||||
func Init(rawConfig json.RawMessage) error {
|
||||
var err error
|
||||
|
||||
initOnce.Do(func() {
|
||||
if rawConfig != nil {
|
||||
config.Validate(configSchema, rawConfig)
|
||||
if err = json.Unmarshal(rawConfig, &UIDefaults); err != nil {
|
||||
cclog.Warn("Error while unmarshaling raw config json")
|
||||
return
|
||||
return err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
UIDefaultsMap = make(map[string]any)
|
||||
|
||||
UIDefaultsMap["joblist_usePaging"] = UIDefaults.JobList.UsePaging
|
||||
UIDefaultsMap["joblist_showFootprint"] = UIDefaults.JobList.ShowFootprint
|
||||
UIDefaultsMap["nodelist_usePaging"] = UIDefaults.NodeList.UsePaging
|
||||
UIDefaultsMap["jobview_showPolarPlot"] = UIDefaults.JobView.ShowPolarPlot
|
||||
UIDefaultsMap["jobview_showFootprint"] = UIDefaults.JobView.ShowFootprint
|
||||
UIDefaultsMap["jobview_showRoofline"] = UIDefaults.JobView.ShowRoofline
|
||||
UIDefaultsMap["jobview_showStatTable"] = UIDefaults.JobView.ShowStatTable
|
||||
|
||||
UIDefaultsMap["metricConfig_jobListMetrics"] = UIDefaults.MetricConfig.JobListMetrics
|
||||
UIDefaultsMap["metricConfig_jobViewPlotMetrics"] = UIDefaults.MetricConfig.JobViewPlotMetrics
|
||||
UIDefaultsMap["metricConfig_jobViewTableMetrics"] = UIDefaults.MetricConfig.JobViewTableMetrics
|
||||
|
||||
UIDefaultsMap["plotConfiguration_colorBackground"] = UIDefaults.PlotConfiguration.ColorBackground
|
||||
UIDefaultsMap["plotConfiguration_plotsPerRow"] = UIDefaults.PlotConfiguration.PlotsPerRow
|
||||
UIDefaultsMap["plotConfiguration_lineWidth"] = UIDefaults.PlotConfiguration.LineWidth
|
||||
UIDefaultsMap["plotConfiguration_colorScheme"] = UIDefaults.PlotConfiguration.ColorScheme
|
||||
|
||||
for _, c := range UIDefaults.MetricConfig.Clusters {
|
||||
if c.JobListMetrics != nil {
|
||||
UIDefaultsMap["metricConfig_jobListMetrics:"+c.Name] = c.JobListMetrics
|
||||
}
|
||||
if c.JobViewPlotMetrics != nil {
|
||||
UIDefaultsMap["metricConfig_jobViewPlotMetrics:"+c.Name] = c.JobViewPlotMetrics
|
||||
}
|
||||
if c.JobViewTableMetrics != nil {
|
||||
UIDefaultsMap["metricConfig_jobViewTableMetrics:"+c.Name] = c.JobViewTableMetrics
|
||||
}
|
||||
|
||||
for _, sc := range c.SubClusters {
|
||||
suffix := strings.Join([]string{c.Name, sc.Name}, ":")
|
||||
if sc.JobListMetrics != nil {
|
||||
UIDefaultsMap["metricConfig_jobListMetrics:"+suffix] = sc.JobListMetrics
|
||||
}
|
||||
if sc.JobViewPlotMetrics != nil {
|
||||
UIDefaultsMap["metricConfig_jobViewPlotMetrics:"+suffix] = sc.JobViewPlotMetrics
|
||||
}
|
||||
if sc.JobViewTableMetrics != nil {
|
||||
UIDefaultsMap["metricConfig_jobViewTableMetrics:"+suffix] = sc.JobViewTableMetrics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
89
web/webConfig_test.go
Normal file
89
web/webConfig_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
|
||||
// All rights reserved. This file is part of cc-backend.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
ccconf "github.com/ClusterCockpit/cc-lib/ccConfig"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
fp := "../../configs/config.json"
|
||||
ccconf.Init(fp)
|
||||
cfg := ccconf.GetPackageConfig("web")
|
||||
|
||||
Init(cfg)
|
||||
|
||||
if UIDefaultsMap["nodelist_usePaging"] == false {
|
||||
t.Errorf("wrong option\ngot: %v \nwant: true", UIDefaultsMap["NodeList_UsePaging"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimpleDefaults(t *testing.T) {
|
||||
const s = `{
|
||||
"joblist": {
|
||||
"showFootprint": false
|
||||
}
|
||||
}`
|
||||
|
||||
Init(json.RawMessage(s))
|
||||
|
||||
if UIDefaultsMap["joblist_usePaging"] == true {
|
||||
t.Errorf("wrong option\ngot: %v \nwant: false", UIDefaultsMap["NodeList_UsePaging"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverwrite(t *testing.T) {
|
||||
const s = `{
|
||||
"metricConfig": {
|
||||
"jobListMetrics": ["flops_sp", "flops_dp"],
|
||||
"clusters": [
|
||||
{
|
||||
"name": "fritz",
|
||||
"jobListMetrics": ["flops_any", "mem_bw", "load"],
|
||||
"subClusters": [
|
||||
{
|
||||
"name": "icelake",
|
||||
"jobListMetrics": ["flops_any", "mem_bw", "power", "load"],
|
||||
"jobViewPlotMetrics": ["load"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
Init(json.RawMessage(s))
|
||||
|
||||
fmt.Printf("%+v", UIDefaultsMap)
|
||||
v, ok := UIDefaultsMap["metricConfig_jobListMetrics"].([]string)
|
||||
if ok {
|
||||
if v[0] != "flops_sp" {
|
||||
t.Errorf("wrong metric\ngot: %s \nwant: flops_sp", v[0])
|
||||
}
|
||||
} else {
|
||||
t.Errorf("missing Key\nkey: metricConfig_jobListMetrics")
|
||||
}
|
||||
v, ok = UIDefaultsMap["metricConfig_jobListMetrics:fritz"].([]string)
|
||||
if ok {
|
||||
if v[0] != "flops_any" {
|
||||
t.Errorf("wrong metric\ngot: %s \nwant: flops_any", v[0])
|
||||
}
|
||||
} else {
|
||||
t.Errorf("missing Key\nkey: metricConfig_jobListMetrics:fritz")
|
||||
}
|
||||
v, ok = UIDefaultsMap["metricConfig_jobListMetrics:fritz:icelake"].([]string)
|
||||
if ok {
|
||||
if v[3] != "load" {
|
||||
t.Errorf("wrong metric\ngot: %s \nwant: load", v[3])
|
||||
}
|
||||
} else {
|
||||
t.Errorf("missing Key\nkey: metricConfig_jobListMetrics:fritz:icelake")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user