diff --git a/Makefile b/Makefile index 5702ba1..d7abb18 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ frontend: swagger: $(info ===> GENERATE swagger) - @go run github.com/swaggo/swag/cmd/swag init -d ./internal/api,./pkg/schema -g rest.go -o ./api + @go run github.com/swaggo/swag/cmd/swag init --parseDependency -d ./internal/api -g rest.go -o ./api @mv ./api/docs.go ./internal/api/docs.go graphql: diff --git a/api/swagger.json b/api/swagger.json index 310297f..87bf3ed 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -1254,9 +1254,27 @@ "api.Node": { "type": "object", "properties": { + "cpusAllocated": { + "type": "integer" + }, + "cpusTotal": { + "type": "integer" + }, + "gpusAllocated": { + "type": "integer" + }, + "gpusTotal": { + "type": "integer" + }, "hostname": { "type": "string" }, + "memoryAllocated": { + "type": "integer" + }, + "memoryTotal": { + "type": "integer" + }, "states": { "type": "array", "items": { @@ -1372,7 +1390,8 @@ "energyFootprint": { "type": "object", "additionalProperties": { - "type": "number" + "type": "number", + "format": "float64" } }, "exclusive": { @@ -1384,7 +1403,8 @@ "footprint": { "type": "object", "additionalProperties": { - "type": "number" + "type": "number", + "format": "float64" } }, "id": { @@ -1475,6 +1495,10 @@ "type": "string", "example": "main" }, + "submitTime": { + "type": "integer", + "example": 1649723812 + }, "tags": { "type": "array", "items": { @@ -1540,24 +1564,32 @@ "schema.JobState": { "type": "string", "enum": [ - "running", - "completed", - "failed", + "boot_fail", "cancelled", - "stopped", - "timeout", + "completed", + "deadline", + "failed", + "node_fail", + "out_of_memory", + "pending", "preempted", - "out_of_memory" + "running", + "suspended", + "timeout" ], "x-enum-varnames": [ - "JobStateRunning", - "JobStateCompleted", - "JobStateFailed", + "JobStateBootFail", "JobStateCancelled", - "JobStateStopped", - "JobStateTimeout", + "JobStateCompleted", + "JobStateDeadline", + "JobStateFailed", + "JobStateNodeFail", + "JobStateOutOfMemory", + "JobStatePending", "JobStatePreempted", - "JobStateOutOfMemory" + "JobStateRunning", + "JobStateSuspended", + "JobStateTimeout" ] }, "schema.JobStatistics": { @@ -1756,7 +1788,8 @@ "additionalProperties": { "type": "array", "items": { - "type": "number" + "type": "number", + "format": "float64" } } } diff --git a/api/swagger.yaml b/api/swagger.yaml index 1c57f2d..06caa56 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -113,8 +113,20 @@ definitions: type: object api.Node: properties: + cpusAllocated: + type: integer + cpusTotal: + type: integer + gpusAllocated: + type: integer + gpusTotal: + type: integer hostname: type: string + memoryAllocated: + type: integer + memoryTotal: + type: integer states: items: type: string @@ -192,6 +204,7 @@ definitions: type: number energyFootprint: additionalProperties: + format: float64 type: number type: object exclusive: @@ -201,6 +214,7 @@ definitions: type: integer footprint: additionalProperties: + format: float64 type: number type: object id: @@ -268,6 +282,9 @@ definitions: subCluster: example: main type: string + submitTime: + example: 1649723812 + type: integer tags: items: $ref: '#/definitions/schema.Tag' @@ -311,24 +328,32 @@ definitions: type: object schema.JobState: enum: - - running - - completed - - failed + - boot_fail - cancelled - - stopped - - timeout - - preempted + - completed + - deadline + - failed + - node_fail - out_of_memory + - pending + - preempted + - running + - suspended + - timeout type: string x-enum-varnames: - - JobStateRunning - - JobStateCompleted - - JobStateFailed + - JobStateBootFail - JobStateCancelled - - JobStateStopped - - JobStateTimeout - - JobStatePreempted + - JobStateCompleted + - JobStateDeadline + - JobStateFailed + - JobStateNodeFail - JobStateOutOfMemory + - JobStatePending + - JobStatePreempted + - JobStateRunning + - JobStateSuspended + - JobStateTimeout schema.JobStatistics: description: Specification for job metric statistics. properties: @@ -465,6 +490,7 @@ definitions: percentiles: additionalProperties: items: + format: float64 type: number type: array type: object diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index 871c8dd..56018c3 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -5,6 +5,7 @@ package main import ( + "encoding/json" "fmt" "os" "os/signal" @@ -22,8 +23,9 @@ import ( "github.com/ClusterCockpit/cc-backend/internal/tagger" "github.com/ClusterCockpit/cc-backend/internal/taskManager" "github.com/ClusterCockpit/cc-backend/pkg/archive" - "github.com/ClusterCockpit/cc-backend/pkg/runtimeEnv" + ccconf "github.com/ClusterCockpit/cc-lib/ccConfig" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" + "github.com/ClusterCockpit/cc-lib/runtimeEnv" "github.com/ClusterCockpit/cc-lib/schema" "github.com/ClusterCockpit/cc-lib/util" "github.com/google/gops/agent" @@ -85,14 +87,17 @@ func main() { // Initialize sub-modules and handle command line flags. // The order here is important! - config.Init(flagConfigFile) + ccconf.Init(flagConfigFile) - // As a special case for `db`, allow using an environment variable instead of the value - // stored in the config. This can be done for people having security concerns about storing - // the password for their mysql database in config.json. - if strings.HasPrefix(config.Keys.DB, "env:") { - envvar := strings.TrimPrefix(config.Keys.DB, "env:") - config.Keys.DB = os.Getenv(envvar) + // Load and check main configuration + if cfg := ccconf.GetPackageConfig("main"); cfg != nil { + if clustercfg := ccconf.GetPackageConfig("clusters"); clustercfg != nil { + config.Init(cfg, clustercfg) + } else { + cclog.Abort("Cluster configuration must be present") + } + } else { + cclog.Abort("Main configuration must be present") } if flagMigrateDB { @@ -123,7 +128,12 @@ func main() { if !config.Keys.DisableAuthentication { - auth.Init() + if cfg := ccconf.GetPackageConfig("auth"); cfg != nil { + auth.Init(&cfg) + } else { + cclog.Warn("Authentication disabled due to missing configuration") + auth.Init(nil) + } if flagNewUser != "" { parts := strings.SplitN(flagNewUser, ":", 3) @@ -188,7 +198,12 @@ func main() { cclog.Abort("Error: Arguments '--add-user' and '--del-user' can only be used if authentication is enabled. No changes, exited.") } - if err := archive.Init(config.Keys.Archive, config.Keys.DisableArchive); err != nil { + if archiveCfg := ccconf.GetPackageConfig("archive"); archiveCfg != nil { + err = archive.Init(archiveCfg, config.Keys.DisableArchive) + } else { + err = archive.Init(json.RawMessage(`{\"kind\":\"file\",\"path\":\"./var/job-archive\"}`), config.Keys.DisableArchive) + } + if err != nil { cclog.Abortf("Init: Failed to initialize archive.\nError: %s\n", err.Error()) } @@ -228,7 +243,8 @@ func main() { archiver.Start(repository.GetJobRepository()) - taskManager.Start() + taskManager.Start(ccconf.GetPackageConfig("cron"), + ccconf.GetPackageConfig("archive")) serverInit() var wg sync.WaitGroup diff --git a/cmd/cc-backend/server.go b/cmd/cc-backend/server.go index c01008a..3983268 100644 --- a/cmd/cc-backend/server.go +++ b/cmd/cc-backend/server.go @@ -27,9 +27,9 @@ import ( "github.com/ClusterCockpit/cc-backend/internal/graph" "github.com/ClusterCockpit/cc-backend/internal/graph/generated" "github.com/ClusterCockpit/cc-backend/internal/routerConfig" - "github.com/ClusterCockpit/cc-backend/pkg/runtimeEnv" "github.com/ClusterCockpit/cc-backend/web" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" + "github.com/ClusterCockpit/cc-lib/runtimeEnv" "github.com/gorilla/handlers" "github.com/gorilla/mux" httpSwagger "github.com/swaggo/http-swagger" @@ -93,7 +93,7 @@ func serverInit() { info := map[string]any{} info["hasOpenIDConnect"] = false - if config.Keys.OpenIDConfig != nil { + if auth.Keys.OpenIDConfig != nil { openIDConnect := auth.NewOIDC(authHandle) openIDConnect.RegisterEndpoints(router) info["hasOpenIDConnect"] = true diff --git a/configs/config-demo.json b/configs/config-demo.json index 9425bd2..d388d78 100644 --- a/configs/config-demo.json +++ b/configs/config-demo.json @@ -1,26 +1,19 @@ { - "addr": "127.0.0.1:8080", - "short-running-jobs-duration": 300, - "archive": { - "kind": "file", - "path": "./var/job-archive" + "main": { + "addr": "127.0.0.1:8080", + "short-running-jobs-duration": 300, + "resampling": { + "trigger": 30, + "resolutions": [600, 300, 120, 60] + }, + "apiAllowedIPs": ["*"], + "emission-constant": 317 }, - "jwts": { - "max-age": "2000h" + "auth": { + "jwts": { + "max-age": "2000h" + } }, - "enable-resampling": { - "trigger": 30, - "resolutions": [ - 600, - 300, - 120, - 60 - ] - }, - "apiAllowedIPs": [ - "*" - ], - "emission-constant": 317, "clusters": [ { "name": "fritz", diff --git a/configs/config-mariadb.json b/configs/config-mariadb.json index e068439..38bb8a9 100644 --- a/configs/config-mariadb.json +++ b/configs/config-mariadb.json @@ -12,12 +12,7 @@ "db": "clustercockpit:demo@tcp(127.0.0.1:3306)/clustercockpit", "enable-resampling": { "trigger": 30, - "resolutions": [ - 600, - 300, - 120, - 60 - ] + "resolutions": [600, 300, 120, 60] }, "emission-constant": 317, "clusters": [ diff --git a/configs/config.json b/configs/config.json index f946b20..27c4ce2 100644 --- a/configs/config.json +++ b/configs/config.json @@ -1,24 +1,18 @@ { - "addr": "0.0.0.0:443", - "ldap": { - "url": "ldaps://test", - "user_base": "ou=people,ou=hpc,dc=test,dc=de", - "search_dn": "cn=hpcmonitoring,ou=roadm,ou=profile,ou=hpc,dc=test,dc=de", - "user_bind": "uid={username},ou=people,ou=hpc,dc=test,dc=de", - "user_filter": "(&(objectclass=posixAccount))" + "main": { + "addr": "0.0.0.0:443", + "https-cert-file": "/etc/letsencrypt/live/url/fullchain.pem", + "https-key-file": "/etc/letsencrypt/live/url/privkey.pem", + "user": "clustercockpit", + "group": "clustercockpit", + "validate": false, + "apiAllowedIPs": ["*"], + "short-running-jobs-duration": 300, + "resampling": { + "trigger": 30, + "resolutions": [600, 300, 120, 60] + } }, - "https-cert-file": "/etc/letsencrypt/live/url/fullchain.pem", - "https-key-file": "/etc/letsencrypt/live/url/privkey.pem", - "user": "clustercockpit", - "group": "clustercockpit", - "archive": { - "kind": "file", - "path": "./var/job-archive" - }, - "validate": false, - "apiAllowedIPs": [ - "*" - ], "clusters": [ { "name": "test", @@ -42,21 +36,5 @@ } } } - ], - "jwts": { - "cookieName": "", - "validateUser": false, - "max-age": "2000h", - "trustedIssuer": "" - }, - "enable-resampling": { - "trigger": 30, - "resolutions": [ - 600, - 300, - 120, - 60 - ] - }, - "short-running-jobs-duration": 300 + ] } diff --git a/go.mod b/go.mod index 4b5171c..554ea56 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,11 @@ go 1.23.5 toolchain go1.24.1 require ( - github.com/99designs/gqlgen v0.17.66 - github.com/ClusterCockpit/cc-lib v0.3.0 + github.com/99designs/gqlgen v0.17.78 + github.com/ClusterCockpit/cc-lib v0.7.0 github.com/Masterminds/squirrel v1.5.4 github.com/coreos/go-oidc/v3 v3.12.0 - github.com/expr-lang/expr v1.17.3 + github.com/expr-lang/expr v1.17.5 github.com/go-co-op/gocron/v2 v2.16.0 github.com/go-ldap/ldap/v3 v3.4.10 github.com/go-sql-driver/mysql v1.9.0 @@ -22,15 +22,15 @@ require ( github.com/jmoiron/sqlx v1.4.0 github.com/joho/godotenv v1.5.1 github.com/mattn/go-sqlite3 v1.14.24 - github.com/prometheus/client_golang v1.22.0 - github.com/prometheus/common v0.63.0 + github.com/prometheus/client_golang v1.23.0 + github.com/prometheus/common v0.65.0 github.com/qustavo/sqlhooks/v2 v2.1.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/swaggo/http-swagger v1.3.4 - github.com/swaggo/swag v1.16.4 - github.com/vektah/gqlparser/v2 v2.5.22 - golang.org/x/crypto v0.37.0 - golang.org/x/oauth2 v0.27.0 + github.com/swaggo/swag v1.16.6 + github.com/vektah/gqlparser/v2 v2.5.30 + golang.org/x/crypto v0.40.0 + golang.org/x/oauth2 v0.30.0 golang.org/x/time v0.5.0 ) @@ -41,16 +41,16 @@ require ( github.com/agnivade/levenshtein v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/go-openapi/swag v0.23.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/websocket v1.5.3 // indirect @@ -74,18 +74,19 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sosodev/duration v1.3.1 // indirect github.com/swaggo/files v1.0.1 // indirect - github.com/urfave/cli/v2 v2.27.5 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + github.com/urfave/cli/v2 v2.27.7 // indirect + github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect go.uber.org/atomic v1.11.0 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.39.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.32.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/tools v0.35.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index f3d25ad..6f61908 100644 --- a/go.sum +++ b/go.sum @@ -1,29 +1,33 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/99designs/gqlgen v0.17.66 h1:2/SRc+h3115fCOZeTtsqrB5R5gTGm+8qCAwcrZa+CXA= -github.com/99designs/gqlgen v0.17.66/go.mod h1:gucrb5jK5pgCKzAGuOMMVU9C8PnReecHEHd2UxLQwCg= +github.com/99designs/gqlgen v0.17.78 h1:bhIi7ynrc3js2O8wu1sMQj1YHPENDt3jQGyifoBvoVI= +github.com/99designs/gqlgen v0.17.78/go.mod h1:yI/o31IauG2kX0IsskM4R894OCCG1jXJORhtLQqB7Oc= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/ClusterCockpit/cc-lib v0.3.0 h1:HEWOgnzRM01U10ZFfpiUWMzkLHg5nPdXZqdsiI2q4x0= -github.com/ClusterCockpit/cc-lib v0.3.0/go.mod h1:7CuXVNIJdynMZf6B9v4m54VCbbFg3ZD0tvLw2bVxN0A= +github.com/ClusterCockpit/cc-lib v0.7.0 h1:THuSYrMcn9pSbrMditSI1LMOluq9TnM0/aVId4uK1Hc= +github.com/ClusterCockpit/cc-lib v0.7.0/go.mod h1:TD1PS8pL2RDvEWaqs8VNejoTSm5OawI9Dcc0CTY/yWQ= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0= -github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U= +github.com/NVIDIA/go-nvml v0.12.9-0 h1:e344UK8ZkeMeeLkdQtRhmXRxNf+u532LDZPGMtkdus0= +github.com/NVIDIA/go-nvml v0.12.9-0/go.mod h1:+KNA7c7gIBH7SKSJ1ntlwkfN80zdx8ovl4hrK3LmPt4= +github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= +github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= -github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -32,8 +36,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -49,8 +53,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/expr-lang/expr v1.17.3 h1:myeTTuDFz7k6eFe/JPlep/UsiIjVhG61FMHFu63U7j0= -github.com/expr-lang/expr v1.17.3/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/expr-lang/expr v1.17.5 h1:i1WrMvcdLF249nSNlpQZN1S6NXuW9WaOfF5tPi3aw3k= +github.com/expr-lang/expr v1.17.5/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -67,27 +71,26 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= +github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8= github.com/golang-migrate/migrate/v4 v4.18.2/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -120,6 +123,12 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjwJdUHnwvfjMF71M1iI4= +github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI= +github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU= +github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/influxdata/line-protocol/v2 v2.2.1 h1:EAPkqJ9Km4uAxtMRgUubJyqAr6zgWM0dznKMLRauQRE= +github.com/influxdata/line-protocol/v2 v2.2.1/go.mod h1:DmB3Cnh+3oxmG6LOBIxce4oaL4CPj3OmMPgvauXh+tM= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -144,6 +153,8 @@ github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2E github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -176,6 +187,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/nats.go v1.44.0 h1:ECKVrDLdh/kDPV1g0gAQ+2+m2KprqZK5O/eJAyAnH2M= +github.com/nats-io/nats.go v1.44.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g= +github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0= +github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -185,12 +204,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= -github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/qustavo/sqlhooks/v2 v2.1.0 h1:54yBemHnGHp/7xgT+pxwmIlMSDNYKx5JW5dfRAiCZi0= @@ -222,14 +241,14 @@ github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww= github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ= -github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= -github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= -github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= -github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/vektah/gqlparser/v2 v2.5.22 h1:yaaeJ0fu+nv1vUMW0Hl+aS1eiv1vMfapBNjpffAda1I= -github.com/vektah/gqlparser/v2 v2.5.22/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= +github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= +github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= +github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= +github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE= +github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= @@ -243,6 +262,10 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= @@ -250,17 +273,17 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -272,10 +295,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -283,8 +306,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -296,8 +319,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -316,8 +339,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -326,8 +349,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= @@ -340,5 +363,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/gqlgen.yml b/gqlgen.yml index 3118ec9..c4b3faf 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -32,6 +32,7 @@ resolver: autobind: - "github.com/99designs/gqlgen/graphql/introspection" - "github.com/ClusterCockpit/cc-backend/internal/graph/model" + - "github.com/ClusterCockpit/cc-backend/internal/config" # This section declares type mapping between the GraphQL and go type systems # @@ -83,8 +84,6 @@ models: { model: "github.com/ClusterCockpit/cc-lib/schema.NodeState" } HealthState: { model: "github.com/ClusterCockpit/cc-lib/schema.MonitoringState" } - TimeRange: { model: "github.com/ClusterCockpit/cc-lib/schema.TimeRange" } - IntRange: { model: "github.com/ClusterCockpit/cc-lib/schema.IntRange" } JobMetric: { model: "github.com/ClusterCockpit/cc-lib/schema.JobMetric" } Series: { model: "github.com/ClusterCockpit/cc-lib/schema.Series" } MetricStatistics: diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 9b792c2..9f47a1f 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -27,6 +27,7 @@ import ( "github.com/ClusterCockpit/cc-backend/internal/metricdata" "github.com/ClusterCockpit/cc-backend/internal/repository" "github.com/ClusterCockpit/cc-backend/pkg/archive" + ccconf "github.com/ClusterCockpit/cc-lib/ccConfig" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" "github.com/gorilla/mux" @@ -36,18 +37,22 @@ import ( func setup(t *testing.T) *api.RestApi { const testconfig = `{ + "main": { "addr": "0.0.0.0:8080", "validate": false, + "apiAllowedIPs": [ + "*" + ] + }, "archive": { "kind": "file", "path": "./var/job-archive" }, - "jwts": { - "max-age": "2m" - }, - "apiAllowedIPs": [ - "*" - ], + "auth": { + "jwts": { + "max-age": "2m" + } + }, "clusters": [ { "name": "testcluster", @@ -146,7 +151,18 @@ func setup(t *testing.T) *api.RestApi { t.Fatal(err) } - config.Init(cfgFilePath) + ccconf.Init(cfgFilePath) + + // Load and check main configuration + if cfg := ccconf.GetPackageConfig("main"); cfg != nil { + if clustercfg := ccconf.GetPackageConfig("clusters"); clustercfg != nil { + config.Init(cfg, clustercfg) + } else { + cclog.Abort("Cluster configuration must be present") + } + } else { + cclog.Abort("Main configuration must be present") + } archiveCfg := fmt.Sprintf("{\"kind\": \"file\",\"path\": \"%s\"}", jobarchive) repository.Connect("sqlite3", dbfilepath) @@ -160,7 +176,14 @@ func setup(t *testing.T) *api.RestApi { } archiver.Start(repository.GetJobRepository()) - auth.Init() + + if cfg := ccconf.GetPackageConfig("auth"); cfg != nil { + auth.Init(&cfg) + } else { + cclog.Warn("Authentication disabled due to missing configuration") + auth.Init(nil) + } + graph.Init() return api.New() @@ -274,7 +297,6 @@ func TestRestApi(t *testing.T) { job.NumNodes != 1 || job.NumHWThreads != 8 || job.NumAcc != 0 || - job.Exclusive != 1 || job.MonitoringStatus != 1 || job.SMT != 1 || !reflect.DeepEqual(job.Resources, []*schema.Resource{{Hostname: "host123", HWThreads: []int{0, 1, 2, 3, 4, 5, 6, 7}}}) || diff --git a/internal/api/docs.go b/internal/api/docs.go index d7b8464..50cab92 100644 --- a/internal/api/docs.go +++ b/internal/api/docs.go @@ -1261,9 +1261,27 @@ const docTemplate = `{ "api.Node": { "type": "object", "properties": { + "cpusAllocated": { + "type": "integer" + }, + "cpusTotal": { + "type": "integer" + }, + "gpusAllocated": { + "type": "integer" + }, + "gpusTotal": { + "type": "integer" + }, "hostname": { "type": "string" }, + "memoryAllocated": { + "type": "integer" + }, + "memoryTotal": { + "type": "integer" + }, "states": { "type": "array", "items": { @@ -1379,7 +1397,8 @@ const docTemplate = `{ "energyFootprint": { "type": "object", "additionalProperties": { - "type": "number" + "type": "number", + "format": "float64" } }, "exclusive": { @@ -1391,7 +1410,8 @@ const docTemplate = `{ "footprint": { "type": "object", "additionalProperties": { - "type": "number" + "type": "number", + "format": "float64" } }, "id": { @@ -1482,6 +1502,10 @@ const docTemplate = `{ "type": "string", "example": "main" }, + "submitTime": { + "type": "integer", + "example": 1649723812 + }, "tags": { "type": "array", "items": { @@ -1547,24 +1571,32 @@ const docTemplate = `{ "schema.JobState": { "type": "string", "enum": [ - "running", - "completed", - "failed", + "boot_fail", "cancelled", - "stopped", - "timeout", + "completed", + "deadline", + "failed", + "node_fail", + "out_of_memory", + "pending", "preempted", - "out_of_memory" + "running", + "suspended", + "timeout" ], "x-enum-varnames": [ - "JobStateRunning", - "JobStateCompleted", - "JobStateFailed", + "JobStateBootFail", "JobStateCancelled", - "JobStateStopped", - "JobStateTimeout", + "JobStateCompleted", + "JobStateDeadline", + "JobStateFailed", + "JobStateNodeFail", + "JobStateOutOfMemory", + "JobStatePending", "JobStatePreempted", - "JobStateOutOfMemory" + "JobStateRunning", + "JobStateSuspended", + "JobStateTimeout" ] }, "schema.JobStatistics": { @@ -1763,7 +1795,8 @@ const docTemplate = `{ "additionalProperties": { "type": "array", "items": { - "type": "number" + "type": "number", + "format": "float64" } } } diff --git a/internal/api/job.go b/internal/api/job.go index 7c27a86..2ce2a3a 100644 --- a/internal/api/job.go +++ b/internal/api/job.go @@ -17,6 +17,7 @@ import ( "time" "github.com/ClusterCockpit/cc-backend/internal/archiver" + "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/graph" "github.com/ClusterCockpit/cc-backend/internal/graph/model" "github.com/ClusterCockpit/cc-backend/internal/importer" @@ -143,7 +144,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) { return } ufrom, uto := time.Unix(from, 0), time.Unix(to, 0) - filter.StartTime = &schema.TimeRange{From: &ufrom, To: &uto} + filter.StartTime = &config.TimeRange{From: &ufrom, To: &uto} case "page": x, err := strconv.Atoi(vals[0]) if err != nil { @@ -647,7 +648,7 @@ func (api *RestApi) removeTags(rw http.ResponseWriter, r *http.Request) { // @router /api/jobs/start_job/ [post] func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) { req := schema.Job{ - Exclusive: 1, + Shared: "none", MonitoringStatus: schema.MonitoringStatusRunningOrArchiving, } if err := decode(r.Body, &req); err != nil { diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 333efc0..4275e3b 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -5,10 +5,12 @@ package auth import ( + "bytes" "context" "crypto/rand" "database/sql" "encoding/base64" + "encoding/json" "errors" "fmt" "net" @@ -51,6 +53,14 @@ func getIPUserLimiter(ip, username string) *rate.Limiter { return limiter.(*rate.Limiter) } +type AuthConfig struct { + LdapConfig *LdapConfig `json:"ldap"` + JwtConfig *JWTAuthConfig `json:"jwts"` + OpenIDConfig *OpenIDConfig `json:"oidc"` +} + +var Keys AuthConfig + type Authentication struct { sessionStore *sessions.CookieStore LdapAuth *LdapAuthenticator @@ -87,7 +97,7 @@ func (auth *Authentication) AuthViaSession( }, nil } -func Init() { +func Init(authCfg *json.RawMessage) { initOnce.Do(func() { authInstance = &Authentication{} @@ -111,7 +121,18 @@ func Init() { authInstance.SessionMaxAge = d } - if config.Keys.LdapConfig != nil { + if authCfg == nil { + return + } + + config.Validate(configSchema, *authCfg) + dec := json.NewDecoder(bytes.NewReader(*authCfg)) + dec.DisallowUnknownFields() + if err := dec.Decode(&Keys); err != nil { + cclog.Errorf("error while decoding ldap config: %v", err) + } + + if Keys.LdapConfig != nil { ldapAuth := &LdapAuthenticator{} if err := ldapAuth.Init(); err != nil { cclog.Warn("Error while initializing authentication -> ldapAuth init failed") @@ -123,7 +144,7 @@ func Init() { cclog.Info("Missing LDAP configuration: No LDAP support!") } - if config.Keys.JwtConfig != nil { + if Keys.JwtConfig != nil { authInstance.JwtAuth = &JWTAuthenticator{} if err := authInstance.JwtAuth.Init(); err != nil { cclog.Fatal("Error while initializing authentication -> jwtAuth init failed") @@ -168,11 +189,11 @@ func handleTokenUser(tokenUser *schema.User) { if err != nil && err != sql.ErrNoRows { cclog.Errorf("Error while loading user '%s': %v", tokenUser.Username, err) - } else if err == sql.ErrNoRows && config.Keys.JwtConfig.SyncUserOnLogin { // Adds New User + } else if err == sql.ErrNoRows && Keys.JwtConfig.SyncUserOnLogin { // Adds New User if err := r.AddUser(tokenUser); err != nil { cclog.Errorf("Error while adding user '%s' to DB: %v", tokenUser.Username, err) } - } else if err == nil && config.Keys.JwtConfig.UpdateUserOnLogin { // Update Existing User + } else if err == nil && Keys.JwtConfig.UpdateUserOnLogin { // Update Existing User if err := r.UpdateUser(dbUser, tokenUser); err != nil { cclog.Errorf("Error while updating user '%s' to DB: %v", dbUser.Username, err) } @@ -185,11 +206,11 @@ func handleOIDCUser(OIDCUser *schema.User) { if err != nil && err != sql.ErrNoRows { cclog.Errorf("Error while loading user '%s': %v", OIDCUser.Username, err) - } else if err == sql.ErrNoRows && config.Keys.OpenIDConfig.SyncUserOnLogin { // Adds New User + } else if err == sql.ErrNoRows && Keys.OpenIDConfig.SyncUserOnLogin { // Adds New User if err := r.AddUser(OIDCUser); err != nil { cclog.Errorf("Error while adding user '%s' to DB: %v", OIDCUser.Username, err) } - } else if err == nil && config.Keys.OpenIDConfig.UpdateUserOnLogin { // Update Existing User + } else if err == nil && Keys.OpenIDConfig.UpdateUserOnLogin { // Update Existing User if err := r.UpdateUser(dbUser, OIDCUser); err != nil { cclog.Errorf("Error while updating user '%s' to DB: %v", dbUser.Username, err) } diff --git a/internal/auth/jwt.go b/internal/auth/jwt.go index 2cc2c37..91f92b5 100644 --- a/internal/auth/jwt.go +++ b/internal/auth/jwt.go @@ -13,13 +13,34 @@ import ( "strings" "time" - "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/repository" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" "github.com/golang-jwt/jwt/v5" ) +type JWTAuthConfig struct { + // Specifies for how long a JWT token shall be valid + // as a string parsable by time.ParseDuration(). + MaxAge string `json:"max-age"` + + // Specifies which cookie should be checked for a JWT token (if no authorization header is present) + CookieName string `json:"cookieName"` + + // Deny login for users not in database (but defined in JWT). + // Ignore user roles defined in JWTs ('roles' claim), get them from db. + ValidateUser bool `json:"validateUser"` + + // Specifies which issuer should be accepted when validating external JWTs ('iss' claim) + TrustedIssuer string `json:"trustedIssuer"` + + // Should an non-existent user be added to the DB based on the information in the token + SyncUserOnLogin bool `json:"syncUserOnLogin"` + + // Should an existent user be updated in the DB based on the information in the token + UpdateUserOnLogin bool `json:"updateUserOnLogin"` +} + type JWTAuthenticator struct { publicKey ed25519.PublicKey privateKey ed25519.PrivateKey @@ -62,7 +83,7 @@ func (ja *JWTAuthenticator) AuthViaJWT( return nil, nil } - token, err := jwt.Parse(rawtoken, func(t *jwt.Token) (interface{}, error) { + token, err := jwt.Parse(rawtoken, func(t *jwt.Token) (any, error) { if t.Method != jwt.SigningMethodEdDSA { return nil, errors.New("only Ed25519/EdDSA supported") } @@ -85,7 +106,7 @@ func (ja *JWTAuthenticator) AuthViaJWT( var roles []string // Validate user + roles from JWT against database? - if config.Keys.JwtConfig.ValidateUser { + if Keys.JwtConfig.ValidateUser { ur := repository.GetUserRepository() user, err := ur.GetUser(sub) // Deny any logins for unknown usernames @@ -97,7 +118,7 @@ func (ja *JWTAuthenticator) AuthViaJWT( roles = user.Roles } else { // Extract roles from JWT (if present) - if rawroles, ok := claims["roles"].([]interface{}); ok { + if rawroles, ok := claims["roles"].([]any); ok { for _, rr := range rawroles { if r, ok := rr.(string); ok { roles = append(roles, r) @@ -126,8 +147,8 @@ func (ja *JWTAuthenticator) ProvideJWT(user *schema.User) (string, error) { "roles": user.Roles, "iat": now.Unix(), } - if config.Keys.JwtConfig.MaxAge != "" { - d, err := time.ParseDuration(config.Keys.JwtConfig.MaxAge) + if Keys.JwtConfig.MaxAge != "" { + d, err := time.ParseDuration(Keys.JwtConfig.MaxAge) if err != nil { return "", errors.New("cannot parse max-age config key") } diff --git a/internal/auth/jwtCookieSession.go b/internal/auth/jwtCookieSession.go index 8f6d064..590a408 100644 --- a/internal/auth/jwtCookieSession.go +++ b/internal/auth/jwtCookieSession.go @@ -13,7 +13,6 @@ import ( "net/http" "os" - "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/repository" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" @@ -63,17 +62,16 @@ func (ja *JWTCookieSessionAuthenticator) Init() error { return errors.New("environment variable 'CROSS_LOGIN_JWT_PUBLIC_KEY' not set (cross login token based authentication will not work)") } - jc := config.Keys.JwtConfig // Warn if other necessary settings are not configured - if jc != nil { - if jc.CookieName == "" { + if Keys.JwtConfig != nil { + if Keys.JwtConfig.CookieName == "" { cclog.Info("cookieName for JWTs not configured (cross login via JWT cookie will fail)") return errors.New("cookieName for JWTs not configured (cross login via JWT cookie will fail)") } - if !jc.ValidateUser { + if !Keys.JwtConfig.ValidateUser { cclog.Info("forceJWTValidationViaDatabase not set to true: CC will accept users and roles defined in JWTs regardless of its own database!") } - if jc.TrustedIssuer == "" { + if Keys.JwtConfig.TrustedIssuer == "" { cclog.Info("trustedExternalIssuer for JWTs not configured (cross login via JWT cookie will fail)") return errors.New("trustedExternalIssuer for JWTs not configured (cross login via JWT cookie will fail)") } @@ -92,7 +90,7 @@ func (ja *JWTCookieSessionAuthenticator) CanLogin( rw http.ResponseWriter, r *http.Request, ) (*schema.User, bool) { - jc := config.Keys.JwtConfig + jc := Keys.JwtConfig cookieName := "" if jc.CookieName != "" { cookieName = jc.CookieName @@ -115,7 +113,7 @@ func (ja *JWTCookieSessionAuthenticator) Login( rw http.ResponseWriter, r *http.Request, ) (*schema.User, error) { - jc := config.Keys.JwtConfig + jc := Keys.JwtConfig jwtCookie, err := r.Cookie(jc.CookieName) var rawtoken string @@ -123,7 +121,7 @@ func (ja *JWTCookieSessionAuthenticator) Login( rawtoken = jwtCookie.Value } - token, err := jwt.Parse(rawtoken, func(t *jwt.Token) (interface{}, error) { + token, err := jwt.Parse(rawtoken, func(t *jwt.Token) (any, error) { if t.Method != jwt.SigningMethodEdDSA { return nil, errors.New("only Ed25519/EdDSA supported") } @@ -169,8 +167,8 @@ func (ja *JWTCookieSessionAuthenticator) Login( } } else { var name string - if wrap, ok := claims["name"].(map[string]interface{}); ok { - if vals, ok := wrap["values"].([]interface{}); ok { + if wrap, ok := claims["name"].(map[string]any); ok { + if vals, ok := wrap["values"].([]any); ok { if len(vals) != 0 { name = fmt.Sprintf("%v", vals[0]) @@ -182,7 +180,7 @@ func (ja *JWTCookieSessionAuthenticator) Login( } // Extract roles from JWT (if present) - if rawroles, ok := claims["roles"].([]interface{}); ok { + if rawroles, ok := claims["roles"].([]any); ok { for _, rr := range rawroles { if r, ok := rr.(string); ok { roles = append(roles, r) diff --git a/internal/auth/jwtSession.go b/internal/auth/jwtSession.go index 9c79e72..e0ca7a9 100644 --- a/internal/auth/jwtSession.go +++ b/internal/auth/jwtSession.go @@ -13,7 +13,6 @@ import ( "os" "strings" - "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/repository" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" @@ -60,7 +59,7 @@ func (ja *JWTSessionAuthenticator) Login( rawtoken = r.URL.Query().Get("login-token") } - token, err := jwt.Parse(rawtoken, func(t *jwt.Token) (interface{}, error) { + token, err := jwt.Parse(rawtoken, func(t *jwt.Token) (any, error) { if t.Method == jwt.SigningMethodHS256 || t.Method == jwt.SigningMethodHS512 { return ja.loginTokenKey, nil } @@ -82,7 +81,7 @@ func (ja *JWTSessionAuthenticator) Login( var roles []string projects := make([]string, 0) - if config.Keys.JwtConfig.ValidateUser { + if Keys.JwtConfig.ValidateUser { var err error user, err = repository.GetUserRepository().GetUser(sub) if err != nil && err != sql.ErrNoRows { @@ -96,8 +95,8 @@ func (ja *JWTSessionAuthenticator) Login( } } else { var name string - if wrap, ok := claims["name"].(map[string]interface{}); ok { - if vals, ok := wrap["values"].([]interface{}); ok { + if wrap, ok := claims["name"].(map[string]any); ok { + if vals, ok := wrap["values"].([]any); ok { if len(vals) != 0 { name = fmt.Sprintf("%v", vals[0]) @@ -109,7 +108,7 @@ func (ja *JWTSessionAuthenticator) Login( } // Extract roles from JWT (if present) - if rawroles, ok := claims["roles"].([]interface{}); ok { + if rawroles, ok := claims["roles"].([]any); ok { for _, rr := range rawroles { if r, ok := rr.(string); ok { if schema.IsValidRole(r) { @@ -119,7 +118,7 @@ func (ja *JWTSessionAuthenticator) Login( } } - if rawprojs, ok := claims["projects"].([]interface{}); ok { + if rawprojs, ok := claims["projects"].([]any); ok { for _, pp := range rawprojs { if p, ok := pp.(string); ok { projects = append(projects, p) @@ -138,7 +137,7 @@ func (ja *JWTSessionAuthenticator) Login( AuthSource: schema.AuthViaToken, } - if config.Keys.JwtConfig.SyncUserOnLogin || config.Keys.JwtConfig.UpdateUserOnLogin { + if Keys.JwtConfig.SyncUserOnLogin || Keys.JwtConfig.UpdateUserOnLogin { handleTokenUser(user) } } diff --git a/internal/auth/ldap.go b/internal/auth/ldap.go index d7843e4..bf5dc7a 100644 --- a/internal/auth/ldap.go +++ b/internal/auth/ldap.go @@ -11,13 +11,26 @@ import ( "os" "strings" - "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/repository" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" "github.com/go-ldap/ldap/v3" ) +type LdapConfig struct { + Url string `json:"url"` + UserBase string `json:"user_base"` + SearchDN string `json:"search_dn"` + UserBind string `json:"user_bind"` + UserFilter string `json:"user_filter"` + UserAttr string `json:"username_attr"` + SyncInterval string `json:"sync_interval"` // Parsed using time.ParseDuration. + SyncDelOldUsers bool `json:"sync_del_old_users"` + + // Should an non-existent user be added to the DB if user exists in ldap directory + SyncUserOnLogin bool `json:"syncUserOnLogin"` +} + type LdapAuthenticator struct { syncPassword string UserAttr string @@ -31,10 +44,8 @@ func (la *LdapAuthenticator) Init() error { cclog.Warn("environment variable 'LDAP_ADMIN_PASSWORD' not set (ldap sync will not work)") } - lc := config.Keys.LdapConfig - - if lc.UserAttr != "" { - la.UserAttr = lc.UserAttr + if Keys.LdapConfig.UserAttr != "" { + la.UserAttr = Keys.LdapConfig.UserAttr } else { la.UserAttr = "gecos" } @@ -48,7 +59,7 @@ func (la *LdapAuthenticator) CanLogin( rw http.ResponseWriter, r *http.Request, ) (*schema.User, bool) { - lc := config.Keys.LdapConfig + lc := Keys.LdapConfig if user != nil { if user.AuthSource == schema.AuthViaLDAP { @@ -119,7 +130,7 @@ func (la *LdapAuthenticator) Login( } defer l.Close() - userDn := strings.Replace(config.Keys.LdapConfig.UserBind, "{username}", user.Username, -1) + userDn := strings.Replace(Keys.LdapConfig.UserBind, "{username}", user.Username, -1) if err := l.Bind(userDn, r.FormValue("password")); err != nil { cclog.Errorf("AUTH/LDAP > Authentication for user %s failed: %v", user.Username, err) @@ -134,7 +145,7 @@ func (la *LdapAuthenticator) Sync() error { const IN_LDAP int = 2 const IN_BOTH int = 3 ur := repository.GetUserRepository() - lc := config.Keys.LdapConfig + lc := Keys.LdapConfig users := map[string]int{} usernames, err := ur.GetLdapUsernames() @@ -210,7 +221,7 @@ func (la *LdapAuthenticator) Sync() error { } func (la *LdapAuthenticator) getLdapConnection(admin bool) (*ldap.Conn, error) { - lc := config.Keys.LdapConfig + lc := Keys.LdapConfig conn, err := ldap.DialURL(lc.Url) if err != nil { cclog.Warn("LDAP URL dial failed") diff --git a/internal/auth/oidc.go b/internal/auth/oidc.go index f688aab..fd856fb 100644 --- a/internal/auth/oidc.go +++ b/internal/auth/oidc.go @@ -13,7 +13,6 @@ import ( "os" "time" - "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/repository" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" @@ -22,6 +21,12 @@ import ( "golang.org/x/oauth2" ) +type OpenIDConfig struct { + Provider string `json:"provider"` + SyncUserOnLogin bool `json:"syncUserOnLogin"` + UpdateUserOnLogin bool `json:"updateUserOnLogin"` +} + type OIDC struct { client *oauth2.Config provider *oidc.Provider @@ -49,7 +54,7 @@ func setCallbackCookie(w http.ResponseWriter, r *http.Request, name, value strin } func NewOIDC(a *Authentication) *OIDC { - provider, err := oidc.NewProvider(context.Background(), config.Keys.OpenIDConfig.Provider) + provider, err := oidc.NewProvider(context.Background(), Keys.OpenIDConfig.Provider) if err != nil { cclog.Fatal(err) } @@ -168,7 +173,7 @@ func (oa *OIDC) OAuth2Callback(rw http.ResponseWriter, r *http.Request) { AuthSource: schema.AuthViaOIDC, } - if config.Keys.OpenIDConfig.SyncUserOnLogin || config.Keys.OpenIDConfig.UpdateUserOnLogin { + if Keys.OpenIDConfig.SyncUserOnLogin || Keys.OpenIDConfig.UpdateUserOnLogin { handleOIDCUser(user) } diff --git a/internal/auth/schema.go b/internal/auth/schema.go new file mode 100644 index 0000000..121cc06 --- /dev/null +++ b/internal/auth/schema.go @@ -0,0 +1,95 @@ +// 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 auth + +var configSchema = ` + { + "jwts": { + "description": "For JWT token authentication.", + "type": "object", + "properties": { + "max-age": { + "description": "Configure how long a token is valid. As string parsable by time.ParseDuration()", + "type": "string" + }, + "cookieName": { + "description": "Cookie that should be checked for a JWT token.", + "type": "string" + }, + "validateUser": { + "description": "Deny login for users not in database (but defined in JWT). Overwrite roles in JWT with database roles.", + "type": "boolean" + }, + "trustedIssuer": { + "description": "Issuer that should be accepted when validating external JWTs ", + "type": "string" + }, + "syncUserOnLogin": { + "description": "Add non-existent user to DB at login attempt with values provided in JWT.", + "type": "boolean" + } + }, + "required": ["max-age"] + }, + "oidc": { + "provider": { + "description": "", + "type": "string" + }, + "syncUserOnLogin": { + "description": "", + "type": "boolean" + }, + "updateUserOnLogin": { + "description": "", + "type": "boolean" + }, + "required": ["provider"] + }, + "ldap": { + "description": "For LDAP Authentication and user synchronisation.", + "type": "object", + "properties": { + "url": { + "description": "URL of LDAP directory server.", + "type": "string" + }, + "user_base": { + "description": "Base DN of user tree root.", + "type": "string" + }, + "search_dn": { + "description": "DN for authenticating LDAP admin account with general read rights.", + "type": "string" + }, + "user_bind": { + "description": "Expression used to authenticate users via LDAP bind. Must contain uid={username}.", + "type": "string" + }, + "user_filter": { + "description": "Filter to extract users for syncing.", + "type": "string" + }, + "username_attr": { + "description": "Attribute with full username. Default: gecos", + "type": "string" + }, + "sync_interval": { + "description": "Interval used for syncing local user table with LDAP directory. Parsed using time.ParseDuration.", + "type": "string" + }, + "sync_del_old_users": { + "description": "Delete obsolete users in database.", + "type": "boolean" + }, + "syncUserOnLogin": { + "description": "Add non-existent user to DB at login attempt if user exists in Ldap directory", + "type": "boolean" + } + }, + "required": ["url", "user_base", "search_dn", "user_bind", "user_filter"] + }, + "required": ["jwts"] + }` diff --git a/internal/config/config.go b/internal/config/config.go index bb965b8..7332941 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,25 +7,123 @@ package config import ( "bytes" "encoding/json" - "os" + "time" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" - "github.com/ClusterCockpit/cc-lib/schema" ) -var Keys schema.ProgramConfig = schema.ProgramConfig{ +type ResampleConfig struct { + // Array of resampling target resolutions, in seconds; Example: [600,300,60] + Resolutions []int `json:"resolutions"` + // Trigger next zoom level at less than this many visible datapoints + Trigger int `json:"trigger"` +} + +// Format of the configuration (file). See below for the defaults. +type ProgramConfig struct { + // Address where the http (or https) server will listen on (for example: 'localhost:80'). + Addr string `json:"addr"` + + // Addresses from which secured admin API endpoints can be reached, can be wildcard "*" + ApiAllowedIPs []string `json:"apiAllowedIPs"` + + // Drop root permissions once .env was read and the port was taken. + User string `json:"user"` + Group string `json:"group"` + + // Disable authentication (for everything: API, Web-UI, ...) + DisableAuthentication bool `json:"disable-authentication"` + + // If `embed-static-files` is true (default), the frontend files are directly + // embeded into the go binary and expected to be in web/frontend. Only if + // it is false the files in `static-files` are served instead. + EmbedStaticFiles bool `json:"embed-static-files"` + StaticFiles string `json:"static-files"` + + // 'sqlite3' or 'mysql' (mysql will work for mariadb as well) + DBDriver string `json:"db-driver"` + + // For sqlite3 a filename, for mysql a DSN in this format: https://github.com/go-sql-driver/mysql#dsn-data-source-name (Without query parameters!). + DB string `json:"db"` + + // Keep all metric data in the metric data repositories, + // do not write to the job-archive. + DisableArchive bool `json:"disable-archive"` + + EnableJobTaggers bool `json:"enable-job-taggers"` + + // Validate json input against schema + Validate bool `json:"validate"` + + // If 0 or empty, the session does not expire! + SessionMaxAge string `json:"session-max-age"` + + // If both those options are not empty, use HTTPS using those certificates. + HttpsCertFile string `json:"https-cert-file"` + HttpsKeyFile string `json:"https-key-file"` + + // If not the empty string and `addr` does not end in ":80", + // redirect every request incoming at port 80 to that url. + RedirectHttpTo string `json:"redirect-http-to"` + + // If overwritten, at least all the options in the defaults below must + // be provided! Most options here can be overwritten by the user. + UiDefaults map[string]any `json:"ui-defaults"` + + // Where to store MachineState files + MachineStateDir string `json:"machine-state-dir"` + + // If not zero, automatically mark jobs as stopped running X seconds longer than their walltime. + StopJobsExceedingWalltime int `json:"stop-jobs-exceeding-walltime"` + + // Defines time X in seconds in which jobs are considered to be "short" and will be filtered in specific views. + ShortRunningJobsDuration int `json:"short-running-jobs-duration"` + + // Energy Mix CO2 Emission Constant [g/kWh] + // If entered, displays estimated CO2 emission for job based on jobs totalEnergy + EmissionConstant int `json:"emission-constant"` + + // If exists, will enable dynamic zoom in frontend metric plots using the configured values + EnableResampling *ResampleConfig `json:"resampling"` +} + +type IntRange struct { + From int `json:"from"` + To int `json:"to"` +} + +type TimeRange struct { + From *time.Time `json:"from"` + To *time.Time `json:"to"` + Range string `json:"range,omitempty"` +} + +type FilterRanges struct { + Duration *IntRange `json:"duration"` + NumNodes *IntRange `json:"numNodes"` + StartTime *TimeRange `json:"startTime"` +} + +type ClusterConfig struct { + Name string `json:"name"` + FilterRanges *FilterRanges `json:"filterRanges"` + MetricDataRepository json.RawMessage `json:"metricDataRepository"` +} + +var Clusters []*ClusterConfig + +var Keys ProgramConfig = ProgramConfig{ Addr: "localhost:8080", DisableAuthentication: false, EmbedStaticFiles: true, DBDriver: "sqlite3", DB: "./var/job.db", - Archive: json.RawMessage(`{\"kind\":\"file\",\"path\":\"./var/job-archive\"}`), DisableArchive: false, Validate: false, SessionMaxAge: "168h", StopJobsExceedingWalltime: 0, ShortRunningJobsDuration: 5 * 60, - UiDefaults: map[string]interface{}{ + UiDefaults: map[string]any{ "analysis_view_histogramMetrics": []string{"flops_any", "mem_bw", "mem_used"}, "analysis_view_scatterPlotMetrics": [][]string{{"flops_any", "mem_bw"}, {"flops_any", "cpu_load"}, {"cpu_load", "mem_bw"}}, "job_view_nodestats_selectedMetrics": []string{"flops_any", "mem_bw", "mem_used"}, @@ -49,24 +147,22 @@ var Keys schema.ProgramConfig = schema.ProgramConfig{ }, } -func Init(flagConfigFile string) { - raw, err := os.ReadFile(flagConfigFile) - if err != nil { - if !os.IsNotExist(err) { - cclog.Abortf("Config Init: Could not read config file '%s'.\nError: %s\n", flagConfigFile, err.Error()) - } - } else { - if err := schema.Validate(schema.Config, bytes.NewReader(raw)); err != nil { - cclog.Abortf("Config Init: Could not validate config file '%s'.\nError: %s\n", flagConfigFile, err.Error()) - } - dec := json.NewDecoder(bytes.NewReader(raw)) - dec.DisallowUnknownFields() - if err := dec.Decode(&Keys); err != nil { - cclog.Abortf("Config Init: Could not decode config file '%s'.\nError: %s\n", flagConfigFile, err.Error()) - } +func Init(mainConfig json.RawMessage, clusterConfig json.RawMessage) { + Validate(configSchema, mainConfig) + dec := json.NewDecoder(bytes.NewReader(mainConfig)) + dec.DisallowUnknownFields() + if err := dec.Decode(&Keys); err != nil { + cclog.Abortf("Config Init: Could not decode config file '%s'.\nError: %s\n", mainConfig, err.Error()) + } - if Keys.Clusters == nil || len(Keys.Clusters) < 1 { - cclog.Abort("Config Init: At least one cluster required in config. Exited with error.") - } + Validate(clustersSchema, clusterConfig) + dec = json.NewDecoder(bytes.NewReader(clusterConfig)) + dec.DisallowUnknownFields() + if err := dec.Decode(&Clusters); err != nil { + cclog.Abortf("Config Init: Could not decode config file '%s'.\nError: %s\n", mainConfig, err.Error()) + } + + if Clusters == nil || len(Clusters) < 1 { + cclog.Abort("Config Init: At least one cluster required in config. Exited with error.") } } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 993b6f0..7dc76c3 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -6,11 +6,24 @@ package config import ( "testing" + + ccconf "github.com/ClusterCockpit/cc-lib/ccConfig" + cclog "github.com/ClusterCockpit/cc-lib/ccLogger" ) func TestInit(t *testing.T) { fp := "../../configs/config.json" - Init(fp) + ccconf.Init(fp) + if cfg := ccconf.GetPackageConfig("main"); cfg != nil { + if clustercfg := ccconf.GetPackageConfig("clusters"); clustercfg != nil { + Init(cfg, clustercfg) + } else { + cclog.Abort("Cluster configuration must be present") + } + } else { + cclog.Abort("Main configuration must be present") + } + if Keys.Addr != "0.0.0.0:443" { t.Errorf("wrong addr\ngot: %s \nwant: 0.0.0.0:443", Keys.Addr) } @@ -18,7 +31,17 @@ func TestInit(t *testing.T) { func TestInitMinimal(t *testing.T) { fp := "../../configs/config-demo.json" - Init(fp) + ccconf.Init(fp) + if cfg := ccconf.GetPackageConfig("main"); cfg != nil { + if clustercfg := ccconf.GetPackageConfig("clusters"); clustercfg != nil { + Init(cfg, clustercfg) + } else { + cclog.Abort("Cluster configuration must be present") + } + } else { + cclog.Abort("Main configuration must be present") + } + if Keys.Addr != "127.0.0.1:8080" { t.Errorf("wrong addr\ngot: %s \nwant: 127.0.0.1:8080", Keys.Addr) } diff --git a/internal/config/schema.go b/internal/config/schema.go new file mode 100644 index 0000000..37d662a --- /dev/null +++ b/internal/config/schema.go @@ -0,0 +1,199 @@ +// 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 config + +var configSchema = ` + { + "type": "object", + "properties": { + "addr": { + "description": "Address where the http (or https) server will listen on (for example: 'localhost:80').", + "type": "string" + }, + "apiAllowedIPs": { + "description": "Addresses from which secured API endpoints can be reached", + "type": "array", + "items": { + "type": "string" + } + }, + "user": { + "description": "Drop root permissions once .env was read and the port was taken. Only applicable if using privileged port.", + "type": "string" + }, + "group": { + "description": "Drop root permissions once .env was read and the port was taken. Only applicable if using privileged port.", + "type": "string" + }, + "disable-authentication": { + "description": "Disable authentication (for everything: API, Web-UI, ...).", + "type": "boolean" + }, + "embed-static-files": { + "description": "If all files in web/frontend/public should be served from within the binary itself (they are embedded) or not.", + "type": "boolean" + }, + "static-files": { + "description": "Folder where static assets can be found, if embed-static-files is false.", + "type": "string" + }, + "db": { + "description": "For sqlite3 a filename, for mysql a DSN in this format: https://github.com/go-sql-driver/mysql#dsn-data-source-name (Without query parameters!).", + "type": "string" + }, + "disable-archive": { + "description": "Keep all metric data in the metric data repositories, do not write to the job-archive.", + "type": "boolean" + }, + "enable-job-taggers": { + "description": "Turn on automatic application and jobclass taggers", + "type": "boolean" + }, + "validate": { + "description": "Validate all input json documents against json schema.", + "type": "boolean" + }, + "session-max-age": { + "description": "Specifies for how long a session shall be valid as a string parsable by time.ParseDuration(). If 0 or empty, the session/token does not expire!", + "type": "string" + }, + "https-cert-file": { + "description": "Filepath to SSL certificate. If also https-key-file is set use HTTPS using those certificates.", + "type": "string" + }, + "https-key-file": { + "description": "Filepath to SSL key file. If also https-cert-file is set use HTTPS using those certificates.", + "type": "string" + }, + "redirect-http-to": { + "description": "If not the empty string and addr does not end in :80, redirect every request incoming at port 80 to that url.", + "type": "string" + }, + "stop-jobs-exceeding-walltime": { + "description": "If not zero, automatically mark jobs as stopped running X seconds longer than their walltime. Only applies if walltime is set for job.", + "type": "integer" + }, + "short-running-jobs-duration": { + "description": "Do not show running jobs shorter than X seconds.", + "type": "integer" + }, + "emission-constant": { + "description": ".", + "type": "integer" + }, + "cron-frequency": { + "description": "Frequency of cron job workers.", + "type": "object", + "properties": { + "duration-worker": { + "description": "Duration Update Worker [Defaults to '5m']", + "type": "string" + }, + "footprint-worker": { + "description": "Metric-Footprint Update Worker [Defaults to '10m']", + "type": "string" + } + } + }, + "enable-resampling": { + "description": "Enable dynamic zoom in frontend metric plots.", + "type": "object", + "properties": { + "trigger": { + "description": "Trigger next zoom level at less than this many visible datapoints.", + "type": "integer" + }, + "resolutions": { + "description": "Array of resampling target resolutions, in seconds.", + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": ["trigger", "resolutions"] + } + }, + "required": ["apiAllowedIPs"] + }` + +var clustersSchema = ` + { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "The name of the cluster.", + "type": "string" + }, + "metricDataRepository": { + "description": "Type of the metric data repository for this cluster", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": ["influxdb", "prometheus", "cc-metric-store", "test"] + }, + "url": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": ["kind", "url"] + }, + "filterRanges": { + "description": "This option controls the slider ranges for the UI controls of numNodes, duration, and startTime.", + "type": "object", + "properties": { + "numNodes": { + "description": "UI slider range for number of nodes", + "type": "object", + "properties": { + "from": { + "type": "integer" + }, + "to": { + "type": "integer" + } + }, + "required": ["from", "to"] + }, + "duration": { + "description": "UI slider range for duration", + "type": "object", + "properties": { + "from": { + "type": "integer" + }, + "to": { + "type": "integer" + } + }, + "required": ["from", "to"] + }, + "startTime": { + "description": "UI slider range for start time", + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time" + }, + "to": { + "type": "null" + } + }, + "required": ["from", "to"] + } + }, + "required": ["numNodes", "duration", "startTime"] + } + }, + "required": ["name", "metricDataRepository", "filterRanges"], + "minItems": 1 + }}` diff --git a/internal/config/validate.go b/internal/config/validate.go new file mode 100644 index 0000000..1614488 --- /dev/null +++ b/internal/config/validate.go @@ -0,0 +1,28 @@ +// 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 config + +import ( + "encoding/json" + + cclog "github.com/ClusterCockpit/cc-lib/ccLogger" + "github.com/santhosh-tekuri/jsonschema/v5" +) + +func Validate(schema string, instance json.RawMessage) { + sch, err := jsonschema.CompileString("schema.json", schema) + if err != nil { + cclog.Fatalf("%#v", err) + } + + var v any + if err := json.Unmarshal([]byte(instance), &v); err != nil { + cclog.Fatal(err) + } + + if err = sch.Validate(v); err != nil { + cclog.Fatalf("%#v", err) + } +} diff --git a/internal/graph/generated/generated.go b/internal/graph/generated/generated.go index ff4469a..011e396 100644 --- a/internal/graph/generated/generated.go +++ b/internal/graph/generated/generated.go @@ -14,6 +14,7 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" + "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/graph/model" "github.com/ClusterCockpit/cc-lib/schema" gqlparser "github.com/vektah/gqlparser/v2" @@ -426,6 +427,8 @@ type ClusterResolver interface { type JobResolver interface { StartTime(ctx context.Context, obj *schema.Job) (*time.Time, error) + Exclusive(ctx context.Context, obj *schema.Job) (int, error) + Tags(ctx context.Context, obj *schema.Job) ([]*schema.Tag, error) ConcurrentJobs(ctx context.Context, obj *schema.Job) (*model.JobLinkResultList, error) @@ -490,7 +493,7 @@ func (e *executableSchema) Schema() *ast.Schema { return parsedSchema } -func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]any) (int, bool) { +func (e *executableSchema) Complexity(ctx context.Context, typeName, field string, childComplexity int, rawArgs map[string]any) (int, bool) { ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -1375,7 +1378,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Mutation_addTagsToJob_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_addTagsToJob_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1387,7 +1390,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Mutation_createTag_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_createTag_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1399,7 +1402,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Mutation_deleteTag_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_deleteTag_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1411,7 +1414,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Mutation_removeTagFromList_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_removeTagFromList_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1423,7 +1426,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Mutation_removeTagsFromJob_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_removeTagsFromJob_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1435,7 +1438,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Mutation_updateConfiguration_args(context.TODO(), rawArgs) + args, err := ec.field_Mutation_updateConfiguration_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1629,7 +1632,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_allocatedNodes_args(context.TODO(), rawArgs) + args, err := ec.field_Query_allocatedNodes_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1655,7 +1658,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_job_args(context.TODO(), rawArgs) + args, err := ec.field_Query_job_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1667,7 +1670,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_jobMetrics_args(context.TODO(), rawArgs) + args, err := ec.field_Query_jobMetrics_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1679,7 +1682,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_jobStats_args(context.TODO(), rawArgs) + args, err := ec.field_Query_jobStats_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1691,7 +1694,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_jobs_args(context.TODO(), rawArgs) + args, err := ec.field_Query_jobs_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1703,7 +1706,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_jobsFootprints_args(context.TODO(), rawArgs) + args, err := ec.field_Query_jobsFootprints_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1715,7 +1718,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_jobsMetricStats_args(context.TODO(), rawArgs) + args, err := ec.field_Query_jobsMetricStats_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1727,7 +1730,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_jobsStatistics_args(context.TODO(), rawArgs) + args, err := ec.field_Query_jobsStatistics_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1739,7 +1742,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_node_args(context.TODO(), rawArgs) + args, err := ec.field_Query_node_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1751,7 +1754,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_nodeMetrics_args(context.TODO(), rawArgs) + args, err := ec.field_Query_nodeMetrics_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1763,7 +1766,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_nodeMetricsList_args(context.TODO(), rawArgs) + args, err := ec.field_Query_nodeMetricsList_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1775,7 +1778,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_nodeStates_args(context.TODO(), rawArgs) + args, err := ec.field_Query_nodeStates_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1787,7 +1790,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_nodes_args(context.TODO(), rawArgs) + args, err := ec.field_Query_nodes_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1799,7 +1802,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_rooflineHeatmap_args(context.TODO(), rawArgs) + args, err := ec.field_Query_rooflineHeatmap_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1811,7 +1814,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_scopedJobStats_args(context.TODO(), rawArgs) + args, err := ec.field_Query_scopedJobStats_args(ctx, rawArgs) if err != nil { return 0, false } @@ -1830,7 +1833,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - args, err := ec.field_Query_user_args(context.TODO(), rawArgs) + args, err := ec.field_Query_user_args(ctx, rawArgs) if err != nil { return 0, false } @@ -2846,7 +2849,7 @@ type JobsStatistics { totalUsers: Int! # if *not* User-Statistics: Number of active users (based on running jobs) totalJobs: Int! # Number of jobs runningJobs: Int! # Number of running jobs - shortJobs: Int! # Number of jobs with a duration of less than duration + shortJobs: Int! # Number of jobs with a duration of less than config'd ShortRunningJobsDuration totalWalltime: Int! # Sum of the duration of all matched jobs in hours totalNodes: Int! # Sum of the nodes of all matched jobs totalNodeHours: Int! # Sum of the node hours of all matched jobs @@ -2876,1701 +2879,504 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) func (ec *executionContext) field_Mutation_addTagsToJob_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Mutation_addTagsToJob_argsJob(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "job", ec.unmarshalNID2string) if err != nil { return nil, err } args["job"] = arg0 - arg1, err := ec.field_Mutation_addTagsToJob_argsTagIds(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "tagIds", ec.unmarshalNID2ᚕstringᚄ) if err != nil { return nil, err } args["tagIds"] = arg1 return args, nil } -func (ec *executionContext) field_Mutation_addTagsToJob_argsJob( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["job"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("job")) - if tmp, ok := rawArgs["job"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Mutation_addTagsToJob_argsTagIds( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["tagIds"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("tagIds")) - if tmp, ok := rawArgs["tagIds"]; ok { - return ec.unmarshalNID2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Mutation_createTag_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Mutation_createTag_argsType(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "type", ec.unmarshalNString2string) if err != nil { return nil, err } args["type"] = arg0 - arg1, err := ec.field_Mutation_createTag_argsName(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "name", ec.unmarshalNString2string) if err != nil { return nil, err } args["name"] = arg1 - arg2, err := ec.field_Mutation_createTag_argsScope(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "scope", ec.unmarshalNString2string) if err != nil { return nil, err } args["scope"] = arg2 return args, nil } -func (ec *executionContext) field_Mutation_createTag_argsType( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["type"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) - if tmp, ok := rawArgs["type"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Mutation_createTag_argsName( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["name"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - if tmp, ok := rawArgs["name"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Mutation_createTag_argsScope( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["scope"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scope")) - if tmp, ok := rawArgs["scope"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Mutation_deleteTag_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Mutation_deleteTag_argsID(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "id", ec.unmarshalNID2string) if err != nil { return nil, err } args["id"] = arg0 return args, nil } -func (ec *executionContext) field_Mutation_deleteTag_argsID( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["id"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - if tmp, ok := rawArgs["id"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Mutation_removeTagFromList_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Mutation_removeTagFromList_argsTagIds(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tagIds", ec.unmarshalNID2ᚕstringᚄ) if err != nil { return nil, err } args["tagIds"] = arg0 return args, nil } -func (ec *executionContext) field_Mutation_removeTagFromList_argsTagIds( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["tagIds"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("tagIds")) - if tmp, ok := rawArgs["tagIds"]; ok { - return ec.unmarshalNID2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Mutation_removeTagsFromJob_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Mutation_removeTagsFromJob_argsJob(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "job", ec.unmarshalNID2string) if err != nil { return nil, err } args["job"] = arg0 - arg1, err := ec.field_Mutation_removeTagsFromJob_argsTagIds(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "tagIds", ec.unmarshalNID2ᚕstringᚄ) if err != nil { return nil, err } args["tagIds"] = arg1 return args, nil } -func (ec *executionContext) field_Mutation_removeTagsFromJob_argsJob( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["job"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("job")) - if tmp, ok := rawArgs["job"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Mutation_removeTagsFromJob_argsTagIds( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["tagIds"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("tagIds")) - if tmp, ok := rawArgs["tagIds"]; ok { - return ec.unmarshalNID2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Mutation_updateConfiguration_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Mutation_updateConfiguration_argsName(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "name", ec.unmarshalNString2string) if err != nil { return nil, err } args["name"] = arg0 - arg1, err := ec.field_Mutation_updateConfiguration_argsValue(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "value", ec.unmarshalNString2string) if err != nil { return nil, err } args["value"] = arg1 return args, nil } -func (ec *executionContext) field_Mutation_updateConfiguration_argsName( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["name"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - if tmp, ok := rawArgs["name"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Mutation_updateConfiguration_argsValue( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["value"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) - if tmp, ok := rawArgs["value"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query___type_argsName(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "name", ec.unmarshalNString2string) if err != nil { return nil, err } args["name"] = arg0 return args, nil } -func (ec *executionContext) field_Query___type_argsName( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["name"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - if tmp, ok := rawArgs["name"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_allocatedNodes_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_allocatedNodes_argsCluster(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "cluster", ec.unmarshalNString2string) if err != nil { return nil, err } args["cluster"] = arg0 return args, nil } -func (ec *executionContext) field_Query_allocatedNodes_argsCluster( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["cluster"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) - if tmp, ok := rawArgs["cluster"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_jobMetrics_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_jobMetrics_argsID(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "id", ec.unmarshalNID2string) if err != nil { return nil, err } args["id"] = arg0 - arg1, err := ec.field_Query_jobMetrics_argsMetrics(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg1 - arg2, err := ec.field_Query_jobMetrics_argsScopes(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "scopes", ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ) if err != nil { return nil, err } args["scopes"] = arg2 - arg3, err := ec.field_Query_jobMetrics_argsResolution(ctx, rawArgs) + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "resolution", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["resolution"] = arg3 return args, nil } -func (ec *executionContext) field_Query_jobMetrics_argsID( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["id"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - if tmp, ok := rawArgs["id"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobMetrics_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobMetrics_argsScopes( - ctx context.Context, - rawArgs map[string]any, -) ([]schema.MetricScope, error) { - if _, ok := rawArgs["scopes"]; !ok { - var zeroVal []schema.MetricScope - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) - if tmp, ok := rawArgs["scopes"]; ok { - return ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ(ctx, tmp) - } - - var zeroVal []schema.MetricScope - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobMetrics_argsResolution( - ctx context.Context, - rawArgs map[string]any, -) (*int, error) { - if _, ok := rawArgs["resolution"]; !ok { - var zeroVal *int - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("resolution")) - if tmp, ok := rawArgs["resolution"]; ok { - return ec.unmarshalOInt2ᚖint(ctx, tmp) - } - - var zeroVal *int - return zeroVal, nil -} func (ec *executionContext) field_Query_jobStats_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_jobStats_argsID(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "id", ec.unmarshalNID2string) if err != nil { return nil, err } args["id"] = arg0 - arg1, err := ec.field_Query_jobStats_argsMetrics(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg1 return args, nil } -func (ec *executionContext) field_Query_jobStats_argsID( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["id"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - if tmp, ok := rawArgs["id"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobStats_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Query_job_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_job_argsID(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "id", ec.unmarshalNID2string) if err != nil { return nil, err } args["id"] = arg0 return args, nil } -func (ec *executionContext) field_Query_job_argsID( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["id"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - if tmp, ok := rawArgs["id"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_jobsFootprints_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_jobsFootprints_argsFilter(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ) if err != nil { return nil, err } args["filter"] = arg0 - arg1, err := ec.field_Query_jobsFootprints_argsMetrics(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalNString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg1 return args, nil } -func (ec *executionContext) field_Query_jobsFootprints_argsFilter( - ctx context.Context, - rawArgs map[string]any, -) ([]*model.JobFilter, error) { - if _, ok := rawArgs["filter"]; !ok { - var zeroVal []*model.JobFilter - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - if tmp, ok := rawArgs["filter"]; ok { - return ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - } - - var zeroVal []*model.JobFilter - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsFootprints_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Query_jobsMetricStats_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_jobsMetricStats_argsFilter(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ) if err != nil { return nil, err } args["filter"] = arg0 - arg1, err := ec.field_Query_jobsMetricStats_argsMetrics(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg1 return args, nil } -func (ec *executionContext) field_Query_jobsMetricStats_argsFilter( - ctx context.Context, - rawArgs map[string]any, -) ([]*model.JobFilter, error) { - if _, ok := rawArgs["filter"]; !ok { - var zeroVal []*model.JobFilter - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - if tmp, ok := rawArgs["filter"]; ok { - return ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - } - - var zeroVal []*model.JobFilter - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsMetricStats_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Query_jobsStatistics_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_jobsStatistics_argsFilter(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ) if err != nil { return nil, err } args["filter"] = arg0 - arg1, err := ec.field_Query_jobsStatistics_argsMetrics(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg1 - arg2, err := ec.field_Query_jobsStatistics_argsPage(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "page", ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest) if err != nil { return nil, err } args["page"] = arg2 - arg3, err := ec.field_Query_jobsStatistics_argsSortBy(ctx, rawArgs) + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "sortBy", ec.unmarshalOSortByAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐSortByAggregate) if err != nil { return nil, err } args["sortBy"] = arg3 - arg4, err := ec.field_Query_jobsStatistics_argsGroupBy(ctx, rawArgs) + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "groupBy", ec.unmarshalOAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐAggregate) if err != nil { return nil, err } args["groupBy"] = arg4 - arg5, err := ec.field_Query_jobsStatistics_argsNumDurationBins(ctx, rawArgs) + arg5, err := graphql.ProcessArgField(ctx, rawArgs, "numDurationBins", ec.unmarshalOString2ᚖstring) if err != nil { return nil, err } args["numDurationBins"] = arg5 - arg6, err := ec.field_Query_jobsStatistics_argsNumMetricBins(ctx, rawArgs) + arg6, err := graphql.ProcessArgField(ctx, rawArgs, "numMetricBins", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["numMetricBins"] = arg6 return args, nil } -func (ec *executionContext) field_Query_jobsStatistics_argsFilter( - ctx context.Context, - rawArgs map[string]any, -) ([]*model.JobFilter, error) { - if _, ok := rawArgs["filter"]; !ok { - var zeroVal []*model.JobFilter - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - if tmp, ok := rawArgs["filter"]; ok { - return ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - } - - var zeroVal []*model.JobFilter - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsStatistics_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsStatistics_argsPage( - ctx context.Context, - rawArgs map[string]any, -) (*model.PageRequest, error) { - if _, ok := rawArgs["page"]; !ok { - var zeroVal *model.PageRequest - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - if tmp, ok := rawArgs["page"]; ok { - return ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) - } - - var zeroVal *model.PageRequest - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsStatistics_argsSortBy( - ctx context.Context, - rawArgs map[string]any, -) (*model.SortByAggregate, error) { - if _, ok := rawArgs["sortBy"]; !ok { - var zeroVal *model.SortByAggregate - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("sortBy")) - if tmp, ok := rawArgs["sortBy"]; ok { - return ec.unmarshalOSortByAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐSortByAggregate(ctx, tmp) - } - - var zeroVal *model.SortByAggregate - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsStatistics_argsGroupBy( - ctx context.Context, - rawArgs map[string]any, -) (*model.Aggregate, error) { - if _, ok := rawArgs["groupBy"]; !ok { - var zeroVal *model.Aggregate - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("groupBy")) - if tmp, ok := rawArgs["groupBy"]; ok { - return ec.unmarshalOAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐAggregate(ctx, tmp) - } - - var zeroVal *model.Aggregate - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsStatistics_argsNumDurationBins( - ctx context.Context, - rawArgs map[string]any, -) (*string, error) { - if _, ok := rawArgs["numDurationBins"]; !ok { - var zeroVal *string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("numDurationBins")) - if tmp, ok := rawArgs["numDurationBins"]; ok { - return ec.unmarshalOString2ᚖstring(ctx, tmp) - } - - var zeroVal *string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobsStatistics_argsNumMetricBins( - ctx context.Context, - rawArgs map[string]any, -) (*int, error) { - if _, ok := rawArgs["numMetricBins"]; !ok { - var zeroVal *int - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("numMetricBins")) - if tmp, ok := rawArgs["numMetricBins"]; ok { - return ec.unmarshalOInt2ᚖint(ctx, tmp) - } - - var zeroVal *int - return zeroVal, nil -} func (ec *executionContext) field_Query_jobs_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_jobs_argsFilter(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ) if err != nil { return nil, err } args["filter"] = arg0 - arg1, err := ec.field_Query_jobs_argsPage(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "page", ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest) if err != nil { return nil, err } args["page"] = arg1 - arg2, err := ec.field_Query_jobs_argsOrder(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "order", ec.unmarshalOOrderByInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐOrderByInput) if err != nil { return nil, err } args["order"] = arg2 return args, nil } -func (ec *executionContext) field_Query_jobs_argsFilter( - ctx context.Context, - rawArgs map[string]any, -) ([]*model.JobFilter, error) { - if _, ok := rawArgs["filter"]; !ok { - var zeroVal []*model.JobFilter - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - if tmp, ok := rawArgs["filter"]; ok { - return ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - } - - var zeroVal []*model.JobFilter - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobs_argsPage( - ctx context.Context, - rawArgs map[string]any, -) (*model.PageRequest, error) { - if _, ok := rawArgs["page"]; !ok { - var zeroVal *model.PageRequest - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - if tmp, ok := rawArgs["page"]; ok { - return ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) - } - - var zeroVal *model.PageRequest - return zeroVal, nil -} - -func (ec *executionContext) field_Query_jobs_argsOrder( - ctx context.Context, - rawArgs map[string]any, -) (*model.OrderByInput, error) { - if _, ok := rawArgs["order"]; !ok { - var zeroVal *model.OrderByInput - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("order")) - if tmp, ok := rawArgs["order"]; ok { - return ec.unmarshalOOrderByInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐOrderByInput(ctx, tmp) - } - - var zeroVal *model.OrderByInput - return zeroVal, nil -} func (ec *executionContext) field_Query_nodeMetricsList_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_nodeMetricsList_argsCluster(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "cluster", ec.unmarshalNString2string) if err != nil { return nil, err } args["cluster"] = arg0 - arg1, err := ec.field_Query_nodeMetricsList_argsSubCluster(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subCluster", ec.unmarshalNString2string) if err != nil { return nil, err } args["subCluster"] = arg1 - arg2, err := ec.field_Query_nodeMetricsList_argsNodeFilter(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "nodeFilter", ec.unmarshalNString2string) if err != nil { return nil, err } args["nodeFilter"] = arg2 - arg3, err := ec.field_Query_nodeMetricsList_argsScopes(ctx, rawArgs) + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "scopes", ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ) if err != nil { return nil, err } args["scopes"] = arg3 - arg4, err := ec.field_Query_nodeMetricsList_argsMetrics(ctx, rawArgs) + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg4 - arg5, err := ec.field_Query_nodeMetricsList_argsFrom(ctx, rawArgs) + arg5, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } args["from"] = arg5 - arg6, err := ec.field_Query_nodeMetricsList_argsTo(ctx, rawArgs) + arg6, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } args["to"] = arg6 - arg7, err := ec.field_Query_nodeMetricsList_argsPage(ctx, rawArgs) + arg7, err := graphql.ProcessArgField(ctx, rawArgs, "page", ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest) if err != nil { return nil, err } args["page"] = arg7 - arg8, err := ec.field_Query_nodeMetricsList_argsResolution(ctx, rawArgs) + arg8, err := graphql.ProcessArgField(ctx, rawArgs, "resolution", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["resolution"] = arg8 return args, nil } -func (ec *executionContext) field_Query_nodeMetricsList_argsCluster( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["cluster"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) - if tmp, ok := rawArgs["cluster"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsSubCluster( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["subCluster"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("subCluster")) - if tmp, ok := rawArgs["subCluster"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsNodeFilter( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["nodeFilter"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("nodeFilter")) - if tmp, ok := rawArgs["nodeFilter"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsScopes( - ctx context.Context, - rawArgs map[string]any, -) ([]schema.MetricScope, error) { - if _, ok := rawArgs["scopes"]; !ok { - var zeroVal []schema.MetricScope - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) - if tmp, ok := rawArgs["scopes"]; ok { - return ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ(ctx, tmp) - } - - var zeroVal []schema.MetricScope - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsFrom( - ctx context.Context, - rawArgs map[string]any, -) (time.Time, error) { - if _, ok := rawArgs["from"]; !ok { - var zeroVal time.Time - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("from")) - if tmp, ok := rawArgs["from"]; ok { - return ec.unmarshalNTime2timeᚐTime(ctx, tmp) - } - - var zeroVal time.Time - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsTo( - ctx context.Context, - rawArgs map[string]any, -) (time.Time, error) { - if _, ok := rawArgs["to"]; !ok { - var zeroVal time.Time - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("to")) - if tmp, ok := rawArgs["to"]; ok { - return ec.unmarshalNTime2timeᚐTime(ctx, tmp) - } - - var zeroVal time.Time - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsPage( - ctx context.Context, - rawArgs map[string]any, -) (*model.PageRequest, error) { - if _, ok := rawArgs["page"]; !ok { - var zeroVal *model.PageRequest - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - if tmp, ok := rawArgs["page"]; ok { - return ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) - } - - var zeroVal *model.PageRequest - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetricsList_argsResolution( - ctx context.Context, - rawArgs map[string]any, -) (*int, error) { - if _, ok := rawArgs["resolution"]; !ok { - var zeroVal *int - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("resolution")) - if tmp, ok := rawArgs["resolution"]; ok { - return ec.unmarshalOInt2ᚖint(ctx, tmp) - } - - var zeroVal *int - return zeroVal, nil -} func (ec *executionContext) field_Query_nodeMetrics_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_nodeMetrics_argsCluster(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "cluster", ec.unmarshalNString2string) if err != nil { return nil, err } args["cluster"] = arg0 - arg1, err := ec.field_Query_nodeMetrics_argsNodes(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "nodes", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["nodes"] = arg1 - arg2, err := ec.field_Query_nodeMetrics_argsScopes(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "scopes", ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ) if err != nil { return nil, err } args["scopes"] = arg2 - arg3, err := ec.field_Query_nodeMetrics_argsMetrics(ctx, rawArgs) + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg3 - arg4, err := ec.field_Query_nodeMetrics_argsFrom(ctx, rawArgs) + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } args["from"] = arg4 - arg5, err := ec.field_Query_nodeMetrics_argsTo(ctx, rawArgs) + arg5, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } args["to"] = arg5 return args, nil } -func (ec *executionContext) field_Query_nodeMetrics_argsCluster( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["cluster"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) - if tmp, ok := rawArgs["cluster"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetrics_argsNodes( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["nodes"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("nodes")) - if tmp, ok := rawArgs["nodes"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetrics_argsScopes( - ctx context.Context, - rawArgs map[string]any, -) ([]schema.MetricScope, error) { - if _, ok := rawArgs["scopes"]; !ok { - var zeroVal []schema.MetricScope - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) - if tmp, ok := rawArgs["scopes"]; ok { - return ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ(ctx, tmp) - } - - var zeroVal []schema.MetricScope - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetrics_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetrics_argsFrom( - ctx context.Context, - rawArgs map[string]any, -) (time.Time, error) { - if _, ok := rawArgs["from"]; !ok { - var zeroVal time.Time - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("from")) - if tmp, ok := rawArgs["from"]; ok { - return ec.unmarshalNTime2timeᚐTime(ctx, tmp) - } - - var zeroVal time.Time - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodeMetrics_argsTo( - ctx context.Context, - rawArgs map[string]any, -) (time.Time, error) { - if _, ok := rawArgs["to"]; !ok { - var zeroVal time.Time - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("to")) - if tmp, ok := rawArgs["to"]; ok { - return ec.unmarshalNTime2timeᚐTime(ctx, tmp) - } - - var zeroVal time.Time - return zeroVal, nil -} func (ec *executionContext) field_Query_nodeStates_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_nodeStates_argsFilter(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalONodeFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeFilterᚄ) if err != nil { return nil, err } args["filter"] = arg0 return args, nil } -func (ec *executionContext) field_Query_nodeStates_argsFilter( - ctx context.Context, - rawArgs map[string]any, -) ([]*model.NodeFilter, error) { - if _, ok := rawArgs["filter"]; !ok { - var zeroVal []*model.NodeFilter - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - if tmp, ok := rawArgs["filter"]; ok { - return ec.unmarshalONodeFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeFilterᚄ(ctx, tmp) - } - - var zeroVal []*model.NodeFilter - return zeroVal, nil -} func (ec *executionContext) field_Query_node_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_node_argsID(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "id", ec.unmarshalNID2string) if err != nil { return nil, err } args["id"] = arg0 return args, nil } -func (ec *executionContext) field_Query_node_argsID( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["id"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - if tmp, ok := rawArgs["id"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_nodes_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_nodes_argsFilter(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalONodeFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeFilterᚄ) if err != nil { return nil, err } args["filter"] = arg0 - arg1, err := ec.field_Query_nodes_argsOrder(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "order", ec.unmarshalOOrderByInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐOrderByInput) if err != nil { return nil, err } args["order"] = arg1 return args, nil } -func (ec *executionContext) field_Query_nodes_argsFilter( - ctx context.Context, - rawArgs map[string]any, -) ([]*model.NodeFilter, error) { - if _, ok := rawArgs["filter"]; !ok { - var zeroVal []*model.NodeFilter - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - if tmp, ok := rawArgs["filter"]; ok { - return ec.unmarshalONodeFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐNodeFilterᚄ(ctx, tmp) - } - - var zeroVal []*model.NodeFilter - return zeroVal, nil -} - -func (ec *executionContext) field_Query_nodes_argsOrder( - ctx context.Context, - rawArgs map[string]any, -) (*model.OrderByInput, error) { - if _, ok := rawArgs["order"]; !ok { - var zeroVal *model.OrderByInput - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("order")) - if tmp, ok := rawArgs["order"]; ok { - return ec.unmarshalOOrderByInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐOrderByInput(ctx, tmp) - } - - var zeroVal *model.OrderByInput - return zeroVal, nil -} func (ec *executionContext) field_Query_rooflineHeatmap_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_rooflineHeatmap_argsFilter(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalNJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ) if err != nil { return nil, err } args["filter"] = arg0 - arg1, err := ec.field_Query_rooflineHeatmap_argsRows(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "rows", ec.unmarshalNInt2int) if err != nil { return nil, err } args["rows"] = arg1 - arg2, err := ec.field_Query_rooflineHeatmap_argsCols(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "cols", ec.unmarshalNInt2int) if err != nil { return nil, err } args["cols"] = arg2 - arg3, err := ec.field_Query_rooflineHeatmap_argsMinX(ctx, rawArgs) + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "minX", ec.unmarshalNFloat2float64) if err != nil { return nil, err } args["minX"] = arg3 - arg4, err := ec.field_Query_rooflineHeatmap_argsMinY(ctx, rawArgs) + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "minY", ec.unmarshalNFloat2float64) if err != nil { return nil, err } args["minY"] = arg4 - arg5, err := ec.field_Query_rooflineHeatmap_argsMaxX(ctx, rawArgs) + arg5, err := graphql.ProcessArgField(ctx, rawArgs, "maxX", ec.unmarshalNFloat2float64) if err != nil { return nil, err } args["maxX"] = arg5 - arg6, err := ec.field_Query_rooflineHeatmap_argsMaxY(ctx, rawArgs) + arg6, err := graphql.ProcessArgField(ctx, rawArgs, "maxY", ec.unmarshalNFloat2float64) if err != nil { return nil, err } args["maxY"] = arg6 return args, nil } -func (ec *executionContext) field_Query_rooflineHeatmap_argsFilter( - ctx context.Context, - rawArgs map[string]any, -) ([]*model.JobFilter, error) { - if _, ok := rawArgs["filter"]; !ok { - var zeroVal []*model.JobFilter - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - if tmp, ok := rawArgs["filter"]; ok { - return ec.unmarshalNJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - } - - var zeroVal []*model.JobFilter - return zeroVal, nil -} - -func (ec *executionContext) field_Query_rooflineHeatmap_argsRows( - ctx context.Context, - rawArgs map[string]any, -) (int, error) { - if _, ok := rawArgs["rows"]; !ok { - var zeroVal int - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("rows")) - if tmp, ok := rawArgs["rows"]; ok { - return ec.unmarshalNInt2int(ctx, tmp) - } - - var zeroVal int - return zeroVal, nil -} - -func (ec *executionContext) field_Query_rooflineHeatmap_argsCols( - ctx context.Context, - rawArgs map[string]any, -) (int, error) { - if _, ok := rawArgs["cols"]; !ok { - var zeroVal int - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("cols")) - if tmp, ok := rawArgs["cols"]; ok { - return ec.unmarshalNInt2int(ctx, tmp) - } - - var zeroVal int - return zeroVal, nil -} - -func (ec *executionContext) field_Query_rooflineHeatmap_argsMinX( - ctx context.Context, - rawArgs map[string]any, -) (float64, error) { - if _, ok := rawArgs["minX"]; !ok { - var zeroVal float64 - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("minX")) - if tmp, ok := rawArgs["minX"]; ok { - return ec.unmarshalNFloat2float64(ctx, tmp) - } - - var zeroVal float64 - return zeroVal, nil -} - -func (ec *executionContext) field_Query_rooflineHeatmap_argsMinY( - ctx context.Context, - rawArgs map[string]any, -) (float64, error) { - if _, ok := rawArgs["minY"]; !ok { - var zeroVal float64 - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("minY")) - if tmp, ok := rawArgs["minY"]; ok { - return ec.unmarshalNFloat2float64(ctx, tmp) - } - - var zeroVal float64 - return zeroVal, nil -} - -func (ec *executionContext) field_Query_rooflineHeatmap_argsMaxX( - ctx context.Context, - rawArgs map[string]any, -) (float64, error) { - if _, ok := rawArgs["maxX"]; !ok { - var zeroVal float64 - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("maxX")) - if tmp, ok := rawArgs["maxX"]; ok { - return ec.unmarshalNFloat2float64(ctx, tmp) - } - - var zeroVal float64 - return zeroVal, nil -} - -func (ec *executionContext) field_Query_rooflineHeatmap_argsMaxY( - ctx context.Context, - rawArgs map[string]any, -) (float64, error) { - if _, ok := rawArgs["maxY"]; !ok { - var zeroVal float64 - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("maxY")) - if tmp, ok := rawArgs["maxY"]; ok { - return ec.unmarshalNFloat2float64(ctx, tmp) - } - - var zeroVal float64 - return zeroVal, nil -} func (ec *executionContext) field_Query_scopedJobStats_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_scopedJobStats_argsID(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "id", ec.unmarshalNID2string) if err != nil { return nil, err } args["id"] = arg0 - arg1, err := ec.field_Query_scopedJobStats_argsMetrics(ctx, rawArgs) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "metrics", ec.unmarshalOString2ᚕstringᚄ) if err != nil { return nil, err } args["metrics"] = arg1 - arg2, err := ec.field_Query_scopedJobStats_argsScopes(ctx, rawArgs) + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "scopes", ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ) if err != nil { return nil, err } args["scopes"] = arg2 return args, nil } -func (ec *executionContext) field_Query_scopedJobStats_argsID( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["id"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - if tmp, ok := rawArgs["id"]; ok { - return ec.unmarshalNID2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_scopedJobStats_argsMetrics( - ctx context.Context, - rawArgs map[string]any, -) ([]string, error) { - if _, ok := rawArgs["metrics"]; !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - if tmp, ok := rawArgs["metrics"]; ok { - return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_scopedJobStats_argsScopes( - ctx context.Context, - rawArgs map[string]any, -) ([]schema.MetricScope, error) { - if _, ok := rawArgs["scopes"]; !ok { - var zeroVal []schema.MetricScope - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) - if tmp, ok := rawArgs["scopes"]; ok { - return ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐMetricScopeᚄ(ctx, tmp) - } - - var zeroVal []schema.MetricScope - return zeroVal, nil -} func (ec *executionContext) field_Query_user_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Query_user_argsUsername(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "username", ec.unmarshalNString2string) if err != nil { return nil, err } args["username"] = arg0 return args, nil } -func (ec *executionContext) field_Query_user_argsUsername( - ctx context.Context, - rawArgs map[string]any, -) (string, error) { - if _, ok := rawArgs["username"]; !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("username")) - if tmp, ok := rawArgs["username"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field___Directive_args_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field___Directive_args_argsIncludeDeprecated(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", ec.unmarshalOBoolean2ᚖbool) if err != nil { return nil, err } args["includeDeprecated"] = arg0 return args, nil } -func (ec *executionContext) field___Directive_args_argsIncludeDeprecated( - ctx context.Context, - rawArgs map[string]any, -) (*bool, error) { - if _, ok := rawArgs["includeDeprecated"]; !ok { - var zeroVal *bool - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - if tmp, ok := rawArgs["includeDeprecated"]; ok { - return ec.unmarshalOBoolean2ᚖbool(ctx, tmp) - } - - var zeroVal *bool - return zeroVal, nil -} func (ec *executionContext) field___Field_args_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field___Field_args_argsIncludeDeprecated(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", ec.unmarshalOBoolean2ᚖbool) if err != nil { return nil, err } args["includeDeprecated"] = arg0 return args, nil } -func (ec *executionContext) field___Field_args_argsIncludeDeprecated( - ctx context.Context, - rawArgs map[string]any, -) (*bool, error) { - if _, ok := rawArgs["includeDeprecated"]; !ok { - var zeroVal *bool - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - if tmp, ok := rawArgs["includeDeprecated"]; ok { - return ec.unmarshalOBoolean2ᚖbool(ctx, tmp) - } - - var zeroVal *bool - return zeroVal, nil -} func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field___Type_enumValues_argsIncludeDeprecated(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", ec.unmarshalOBoolean2bool) if err != nil { return nil, err } args["includeDeprecated"] = arg0 return args, nil } -func (ec *executionContext) field___Type_enumValues_argsIncludeDeprecated( - ctx context.Context, - rawArgs map[string]any, -) (bool, error) { - if _, ok := rawArgs["includeDeprecated"]; !ok { - var zeroVal bool - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - if tmp, ok := rawArgs["includeDeprecated"]; ok { - return ec.unmarshalOBoolean2bool(ctx, tmp) - } - - var zeroVal bool - return zeroVal, nil -} func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field___Type_fields_argsIncludeDeprecated(ctx, rawArgs) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", ec.unmarshalOBoolean2bool) if err != nil { return nil, err } args["includeDeprecated"] = arg0 return args, nil } -func (ec *executionContext) field___Type_fields_argsIncludeDeprecated( - ctx context.Context, - rawArgs map[string]any, -) (bool, error) { - if _, ok := rawArgs["includeDeprecated"]; !ok { - var zeroVal bool - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - if tmp, ok := rawArgs["includeDeprecated"]; ok { - return ec.unmarshalOBoolean2bool(ctx, tmp) - } - - var zeroVal bool - return zeroVal, nil -} // endregion ***************************** args.gotpl ***************************** @@ -6449,7 +5255,7 @@ func (ec *executionContext) _Job_exclusive(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return obj.Exclusive, nil + return ec.resolvers.Job().Exclusive(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -6461,17 +5267,17 @@ func (ec *executionContext) _Job_exclusive(ctx context.Context, field graphql.Co } return graphql.Null } - res := resTmp.(int32) + res := resTmp.(int) fc.Result = res - return ec.marshalNInt2int32(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Job_exclusive(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Job", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type Int does not have child fields") }, @@ -13105,6 +11911,8 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -13117,8 +11925,6 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -15728,6 +14534,50 @@ func (ec *executionContext) fieldContext___Directive_description(_ context.Conte return fc, nil } +func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_isRepeatable(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsRepeatable, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_isRepeatable(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Directive_locations(ctx, field) if err != nil { @@ -15841,50 +14691,6 @@ func (ec *executionContext) fieldContext___Directive_args(ctx context.Context, f return fc, nil } -func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Directive_isRepeatable(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.IsRepeatable, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(bool) - fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext___Directive_isRepeatable(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "__Directive", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { fc, err := ec.fieldContext___EnumValue_name(ctx, field) if err != nil { @@ -16254,6 +15060,8 @@ func (ec *executionContext) fieldContext___Field_type(_ context.Context, field g return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -16266,8 +15074,6 @@ func (ec *executionContext) fieldContext___Field_type(_ context.Context, field g return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -16492,6 +15298,8 @@ func (ec *executionContext) fieldContext___InputValue_type(_ context.Context, fi return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -16504,8 +15312,6 @@ func (ec *executionContext) fieldContext___InputValue_type(_ context.Context, fi return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -16727,6 +15533,8 @@ func (ec *executionContext) fieldContext___Schema_types(_ context.Context, field return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -16739,8 +15547,6 @@ func (ec *executionContext) fieldContext___Schema_types(_ context.Context, field return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -16795,6 +15601,8 @@ func (ec *executionContext) fieldContext___Schema_queryType(_ context.Context, f return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -16807,8 +15615,6 @@ func (ec *executionContext) fieldContext___Schema_queryType(_ context.Context, f return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -16860,6 +15666,8 @@ func (ec *executionContext) fieldContext___Schema_mutationType(_ context.Context return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -16872,8 +15680,6 @@ func (ec *executionContext) fieldContext___Schema_mutationType(_ context.Context return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -16925,6 +15731,8 @@ func (ec *executionContext) fieldContext___Schema_subscriptionType(_ context.Con return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -16937,8 +15745,6 @@ func (ec *executionContext) fieldContext___Schema_subscriptionType(_ context.Con return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -16991,12 +15797,12 @@ func (ec *executionContext) fieldContext___Schema_directives(_ context.Context, return ec.fieldContext___Directive_name(ctx, field) case "description": return ec.fieldContext___Directive_description(ctx, field) + case "isRepeatable": + return ec.fieldContext___Directive_isRepeatable(ctx, field) case "locations": return ec.fieldContext___Directive_locations(ctx, field) case "args": return ec.fieldContext___Directive_args(ctx, field) - case "isRepeatable": - return ec.fieldContext___Directive_isRepeatable(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type __Directive", field.Name) }, @@ -17130,6 +15936,47 @@ func (ec *executionContext) fieldContext___Type_description(_ context.Context, f return fc, nil } +func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_specifiedByURL(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.SpecifiedByURL(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_specifiedByURL(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Type_fields(ctx, field) if err != nil { @@ -17238,6 +16085,8 @@ func (ec *executionContext) fieldContext___Type_interfaces(_ context.Context, fi return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -17250,8 +16099,6 @@ func (ec *executionContext) fieldContext___Type_interfaces(_ context.Context, fi return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -17303,6 +16150,8 @@ func (ec *executionContext) fieldContext___Type_possibleTypes(_ context.Context, return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -17315,8 +16164,6 @@ func (ec *executionContext) fieldContext___Type_possibleTypes(_ context.Context, return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -17485,6 +16332,8 @@ func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field return ec.fieldContext___Type_name(ctx, field) case "description": return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) case "fields": return ec.fieldContext___Type_fields(ctx, field) case "interfaces": @@ -17497,8 +16346,6 @@ func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field return ec.fieldContext___Type_inputFields(ctx, field) case "ofType": return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) case "isOneOf": return ec.fieldContext___Type_isOneOf(ctx, field) } @@ -17508,47 +16355,6 @@ func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field return fc, nil } -func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_specifiedByURL(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.SpecifiedByURL(), nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext___Type_specifiedByURL(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "__Type", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) ___Type_isOneOf(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Type_isOneOf(ctx, field) if err != nil { @@ -17628,8 +16434,8 @@ func (ec *executionContext) unmarshalInputFloatRange(ctx context.Context, obj an return it, nil } -func (ec *executionContext) unmarshalInputIntRange(ctx context.Context, obj any) (schema.IntRange, error) { - var it schema.IntRange +func (ec *executionContext) unmarshalInputIntRange(ctx context.Context, obj any) (config.IntRange, error) { + var it config.IntRange asMap := map[string]any{} for k, v := range obj.(map[string]any) { asMap[k] = v @@ -17741,7 +16547,7 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj any it.Partition = data case "duration": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("duration")) - data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐIntRange(ctx, v) + data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋconfigᚐIntRange(ctx, v) if err != nil { return it, err } @@ -17762,28 +16568,28 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj any it.MinRunningFor = data case "numNodes": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("numNodes")) - data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐIntRange(ctx, v) + data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋconfigᚐIntRange(ctx, v) if err != nil { return it, err } it.NumNodes = data case "numAccelerators": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("numAccelerators")) - data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐIntRange(ctx, v) + data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋconfigᚐIntRange(ctx, v) if err != nil { return it, err } it.NumAccelerators = data case "numHWThreads": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("numHWThreads")) - data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐIntRange(ctx, v) + data, err := ec.unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋconfigᚐIntRange(ctx, v) if err != nil { return it, err } it.NumHWThreads = data case "startTime": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("startTime")) - data, err := ec.unmarshalOTimeRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐTimeRange(ctx, v) + data, err := ec.unmarshalOTimeRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋconfigᚐTimeRange(ctx, v) if err != nil { return it, err } @@ -18052,8 +16858,8 @@ func (ec *executionContext) unmarshalInputStringInput(ctx context.Context, obj a return it, nil } -func (ec *executionContext) unmarshalInputTimeRange(ctx context.Context, obj any) (schema.TimeRange, error) { - var it schema.TimeRange +func (ec *executionContext) unmarshalInputTimeRange(ctx context.Context, obj any) (config.TimeRange, error) { + var it config.TimeRange asMap := map[string]any{} for k, v := range obj.(map[string]any) { asMap[k] = v @@ -18717,10 +17523,41 @@ func (ec *executionContext) _Job(ctx context.Context, sel ast.SelectionSet, obj atomic.AddUint32(&out.Invalids, 1) } case "exclusive": - out.Values[i] = ec._Job_exclusive(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Job_exclusive(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "partition": out.Values[i] = ec._Job_partition(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -21429,6 +20266,11 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS } case "description": out.Values[i] = ec.___Directive_description(ctx, field, obj) + case "isRepeatable": + out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "locations": out.Values[i] = ec.___Directive_locations(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -21439,11 +20281,6 @@ func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionS if out.Values[i] == graphql.Null { out.Invalids++ } - case "isRepeatable": - out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -21703,6 +20540,8 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o out.Values[i] = ec.___Type_name(ctx, field, obj) case "description": out.Values[i] = ec.___Type_description(ctx, field, obj) + case "specifiedByURL": + out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) case "fields": out.Values[i] = ec.___Type_fields(ctx, field, obj) case "interfaces": @@ -21715,8 +20554,6 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o out.Values[i] = ec.___Type_inputFields(ctx, field, obj) case "ofType": out.Values[i] = ec.___Type_ofType(ctx, field, obj) - case "specifiedByURL": - out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) case "isOneOf": out.Values[i] = ec.___Type_isOneOf(ctx, field, obj) default: @@ -21762,6 +20599,7 @@ func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v any) ( } func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + _ = sel res := graphql.MarshalBoolean(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -21933,6 +20771,7 @@ func (ec *executionContext) unmarshalNFloat2float64(ctx context.Context, v any) } func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.SelectionSet, v float64) graphql.Marshaler { + _ = sel res := graphql.MarshalFloatContext(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -21944,9 +20783,7 @@ func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.S func (ec *executionContext) unmarshalNFloat2ᚕfloat64ᚄ(ctx context.Context, v any) ([]float64, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]float64, len(vSlice)) for i := range vSlice { @@ -21976,9 +20813,7 @@ func (ec *executionContext) marshalNFloat2ᚕfloat64ᚄ(ctx context.Context, sel func (ec *executionContext) unmarshalNFloat2ᚕᚕfloat64ᚄ(ctx context.Context, v any) ([][]float64, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([][]float64, len(vSlice)) for i := range vSlice { @@ -22125,6 +20960,7 @@ func (ec *executionContext) unmarshalNID2int64(ctx context.Context, v any) (int6 } func (ec *executionContext) marshalNID2int64(ctx context.Context, sel ast.SelectionSet, v int64) graphql.Marshaler { + _ = sel res := graphql.MarshalInt64(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -22140,6 +20976,7 @@ func (ec *executionContext) unmarshalNID2string(ctx context.Context, v any) (str } func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel res := graphql.MarshalID(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -22151,9 +20988,7 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec func (ec *executionContext) unmarshalNID2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]string, len(vSlice)) for i := range vSlice { @@ -22193,6 +21028,7 @@ func (ec *executionContext) marshalNID2ᚖint64(ctx context.Context, sel ast.Sel } return graphql.Null } + _ = sel res := graphql.MarshalInt64(*v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -22208,6 +21044,7 @@ func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v any) (int, } func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + _ = sel res := graphql.MarshalInt(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -22223,6 +21060,7 @@ func (ec *executionContext) unmarshalNInt2int32(ctx context.Context, v any) (int } func (ec *executionContext) marshalNInt2int32(ctx context.Context, sel ast.SelectionSet, v int32) graphql.Marshaler { + _ = sel res := graphql.MarshalInt32(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -22238,6 +21076,7 @@ func (ec *executionContext) unmarshalNInt2int64(ctx context.Context, v any) (int } func (ec *executionContext) marshalNInt2int64(ctx context.Context, sel ast.SelectionSet, v int64) graphql.Marshaler { + _ = sel res := graphql.MarshalInt64(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -22249,9 +21088,7 @@ func (ec *executionContext) marshalNInt2int64(ctx context.Context, sel ast.Selec func (ec *executionContext) unmarshalNInt2ᚕintᚄ(ctx context.Context, v any) ([]int, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]int, len(vSlice)) for i := range vSlice { @@ -22281,9 +21118,7 @@ func (ec *executionContext) marshalNInt2ᚕintᚄ(ctx context.Context, sel ast.S func (ec *executionContext) unmarshalNInt2ᚕᚖintᚄ(ctx context.Context, v any) ([]*int, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]*int, len(vSlice)) for i := range vSlice { @@ -22323,6 +21158,7 @@ func (ec *executionContext) marshalNInt2ᚖint(ctx context.Context, sel ast.Sele } return graphql.Null } + _ = sel res := graphql.MarshalInt(*v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -22388,9 +21224,7 @@ func (ec *executionContext) marshalNJob2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑ func (ec *executionContext) unmarshalNJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx context.Context, v any) ([]*model.JobFilter, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]*model.JobFilter, len(vSlice)) for i := range vSlice { @@ -22860,6 +21694,7 @@ func (ec *executionContext) unmarshalNMonitoringState2githubᚗcomᚋClusterCock } func (ec *executionContext) marshalNMonitoringState2githubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐNodeState(ctx context.Context, sel ast.SelectionSet, v schema.NodeState) graphql.Marshaler { + _ = sel res := graphql.MarshalString(string(v)) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23096,6 +21931,7 @@ func (ec *executionContext) unmarshalNNodeState2string(ctx context.Context, v an } func (ec *executionContext) marshalNNodeState2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23199,9 +22035,7 @@ func (ec *executionContext) marshalNNullableFloat2githubᚗcomᚋClusterCockpit func (ec *executionContext) unmarshalNNullableFloat2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐFloatᚄ(ctx context.Context, v any) ([]schema.Float, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]schema.Float, len(vSlice)) for i := range vSlice { @@ -23357,6 +22191,7 @@ func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) } func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23368,9 +22203,7 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S func (ec *executionContext) unmarshalNString2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]string, len(vSlice)) for i := range vSlice { @@ -23570,6 +22403,7 @@ func (ec *executionContext) unmarshalNTime2timeᚐTime(ctx context.Context, v an } func (ec *executionContext) marshalNTime2timeᚐTime(ctx context.Context, sel ast.SelectionSet, v time.Time) graphql.Marshaler { + _ = sel res := graphql.MarshalTime(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23591,6 +22425,7 @@ func (ec *executionContext) marshalNTime2ᚖtimeᚐTime(ctx context.Context, sel } return graphql.Null } + _ = sel res := graphql.MarshalTime(*v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23672,6 +22507,7 @@ func (ec *executionContext) unmarshalN__DirectiveLocation2string(ctx context.Con } func (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23683,9 +22519,7 @@ func (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Conte func (ec *executionContext) unmarshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) { var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]string, len(vSlice)) for i := range vSlice { @@ -23862,6 +22696,7 @@ func (ec *executionContext) unmarshalN__TypeKind2string(ctx context.Context, v a } func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -23946,6 +22781,8 @@ func (ec *executionContext) marshalOAny2interface(ctx context.Context, sel ast.S if v == nil { return graphql.Null } + _ = sel + _ = ctx res := graphql.MarshalAny(v) return res } @@ -23956,6 +22793,8 @@ func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v any) ( } func (ec *executionContext) marshalOBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + _ = sel + _ = ctx res := graphql.MarshalBoolean(v) return res } @@ -23972,6 +22811,8 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast if v == nil { return graphql.Null } + _ = sel + _ = ctx res := graphql.MarshalBoolean(*v) return res } @@ -24030,6 +22871,7 @@ func (ec *executionContext) unmarshalOFloat2float64(ctx context.Context, v any) } func (ec *executionContext) marshalOFloat2float64(ctx context.Context, sel ast.SelectionSet, v float64) graphql.Marshaler { + _ = sel res := graphql.MarshalFloatContext(v) return graphql.WrapContextMarshaler(ctx, res) } @@ -24102,9 +22944,7 @@ func (ec *executionContext) unmarshalOID2ᚕstringᚄ(ctx context.Context, v any return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]string, len(vSlice)) for i := range vSlice { @@ -24140,9 +22980,7 @@ func (ec *executionContext) unmarshalOInt2ᚕintᚄ(ctx context.Context, v any) return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]int, len(vSlice)) for i := range vSlice { @@ -24178,9 +23016,7 @@ func (ec *executionContext) unmarshalOInt2ᚕᚕintᚄ(ctx context.Context, v an return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([][]int, len(vSlice)) for i := range vSlice { @@ -24216,9 +23052,7 @@ func (ec *executionContext) unmarshalOInt2ᚕᚕᚖintᚄ(ctx context.Context, v return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([][]*int, len(vSlice)) for i := range vSlice { @@ -24261,11 +23095,13 @@ func (ec *executionContext) marshalOInt2ᚖint(ctx context.Context, sel ast.Sele if v == nil { return graphql.Null } + _ = sel + _ = ctx res := graphql.MarshalInt(*v) return res } -func (ec *executionContext) unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐIntRange(ctx context.Context, v any) (*schema.IntRange, error) { +func (ec *executionContext) unmarshalOIntRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋconfigᚐIntRange(ctx context.Context, v any) (*config.IntRange, error) { if v == nil { return nil, nil } @@ -24285,9 +23121,7 @@ func (ec *executionContext) unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCock return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]*model.JobFilter, len(vSlice)) for i := range vSlice { @@ -24312,9 +23146,7 @@ func (ec *executionContext) unmarshalOJobState2ᚕgithubᚗcomᚋClusterCockpit return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]schema.JobState, len(vSlice)) for i := range vSlice { @@ -24397,9 +23229,7 @@ func (ec *executionContext) unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockp return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]schema.MetricScope, len(vSlice)) for i := range vSlice { @@ -24435,9 +23265,7 @@ func (ec *executionContext) unmarshalOMetricStatItem2ᚕᚖgithubᚗcomᚋCluste return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]*model.MetricStatItem, len(vSlice)) for i := range vSlice { @@ -24467,6 +23295,8 @@ func (ec *executionContext) marshalOMonitoringState2ᚖgithubᚗcomᚋClusterCoc if v == nil { return graphql.Null } + _ = sel + _ = ctx res := graphql.MarshalString(string(*v)) return res } @@ -24483,9 +23313,7 @@ func (ec *executionContext) unmarshalONodeFilter2ᚕᚖgithubᚗcomᚋClusterCoc return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]*model.NodeFilter, len(vSlice)) for i := range vSlice { @@ -24510,6 +23338,8 @@ func (ec *executionContext) marshalONodeState2ᚖstring(ctx context.Context, sel if v == nil { return graphql.Null } + _ = sel + _ = ctx res := graphql.MarshalString(*v) return res } @@ -24606,6 +23436,8 @@ func (ec *executionContext) unmarshalOString2string(ctx context.Context, v any) } func (ec *executionContext) marshalOString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel + _ = ctx res := graphql.MarshalString(v) return res } @@ -24615,9 +23447,7 @@ func (ec *executionContext) unmarshalOString2ᚕstringᚄ(ctx context.Context, v return nil, nil } var vSlice []any - if v != nil { - vSlice = graphql.CoerceList(v) - } + vSlice = graphql.CoerceList(v) var err error res := make([]string, len(vSlice)) for i := range vSlice { @@ -24660,6 +23490,8 @@ func (ec *executionContext) marshalOString2ᚖstring(ctx context.Context, sel as if v == nil { return graphql.Null } + _ = sel + _ = ctx res := graphql.MarshalString(*v) return res } @@ -24684,11 +23516,13 @@ func (ec *executionContext) marshalOTime2ᚖtimeᚐTime(ctx context.Context, sel if v == nil { return graphql.Null } + _ = sel + _ = ctx res := graphql.MarshalTime(*v) return res } -func (ec *executionContext) unmarshalOTimeRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑlibᚋschemaᚐTimeRange(ctx context.Context, v any) (*schema.TimeRange, error) { +func (ec *executionContext) unmarshalOTimeRange2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋconfigᚐTimeRange(ctx context.Context, v any) (*config.TimeRange, error) { if v == nil { return nil, nil } diff --git a/internal/graph/model/models_gen.go b/internal/graph/model/models_gen.go index a5fe2a2..c1e8be6 100644 --- a/internal/graph/model/models_gen.go +++ b/internal/graph/model/models_gen.go @@ -3,11 +3,13 @@ package model import ( + "bytes" "fmt" "io" "strconv" "time" + "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-lib/schema" ) @@ -58,13 +60,13 @@ type JobFilter struct { JobName *StringInput `json:"jobName,omitempty"` Cluster *StringInput `json:"cluster,omitempty"` Partition *StringInput `json:"partition,omitempty"` - Duration *schema.IntRange `json:"duration,omitempty"` + Duration *config.IntRange `json:"duration,omitempty"` Energy *FloatRange `json:"energy,omitempty"` MinRunningFor *int `json:"minRunningFor,omitempty"` - NumNodes *schema.IntRange `json:"numNodes,omitempty"` - NumAccelerators *schema.IntRange `json:"numAccelerators,omitempty"` - NumHWThreads *schema.IntRange `json:"numHWThreads,omitempty"` - StartTime *schema.TimeRange `json:"startTime,omitempty"` + NumNodes *config.IntRange `json:"numNodes,omitempty"` + NumAccelerators *config.IntRange `json:"numAccelerators,omitempty"` + NumHWThreads *config.IntRange `json:"numHWThreads,omitempty"` + StartTime *config.TimeRange `json:"startTime,omitempty"` State []schema.JobState `json:"state,omitempty"` MetricStats []*MetricStatItem `json:"metricStats,omitempty"` Exclusive *int `json:"exclusive,omitempty"` @@ -290,6 +292,20 @@ func (e Aggregate) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } +func (e *Aggregate) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e Aggregate) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + type SortByAggregate string const ( @@ -345,6 +361,20 @@ func (e SortByAggregate) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } +func (e *SortByAggregate) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e SortByAggregate) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + type SortDirectionEnum string const ( @@ -385,3 +415,17 @@ func (e *SortDirectionEnum) UnmarshalGQL(v any) error { func (e SortDirectionEnum) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } + +func (e *SortDirectionEnum) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e SortDirectionEnum) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index b993ebb..a7a69c3 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -2,7 +2,7 @@ package graph // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.66 +// Code generated by github.com/99designs/gqlgen version v0.17.78 import ( "context" @@ -35,6 +35,11 @@ func (r *jobResolver) StartTime(ctx context.Context, obj *schema.Job) (*time.Tim return ×tamp, nil } +// Exclusive is the resolver for the exclusive field. +func (r *jobResolver) Exclusive(ctx context.Context, obj *schema.Job) (int, error) { + panic(fmt.Errorf("not implemented: Exclusive - exclusive")) +} + // Tags is the resolver for the tags field. func (r *jobResolver) Tags(ctx context.Context, obj *schema.Job) ([]*schema.Tag, error) { return r.Repo.GetTags(repository.GetUserFromContext(ctx), obj.ID) @@ -43,7 +48,7 @@ func (r *jobResolver) Tags(ctx context.Context, obj *schema.Job) ([]*schema.Tag, // ConcurrentJobs is the resolver for the concurrentJobs field. func (r *jobResolver) ConcurrentJobs(ctx context.Context, obj *schema.Job) (*model.JobLinkResultList, error) { // FIXME: Make the hardcoded duration configurable - if obj.Exclusive != 1 && obj.Duration > 600 { + if obj.Shared != "none" && obj.Duration > 600 { return r.Repo.FindConcurrentJobs(ctx, obj) } diff --git a/internal/importer/handleImport.go b/internal/importer/handleImport.go index 71c4d24..f527781 100644 --- a/internal/importer/handleImport.go +++ b/internal/importer/handleImport.go @@ -43,7 +43,7 @@ func HandleImportFlag(flag string) error { dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() job := schema.Job{ - Exclusive: 1, + Shared: "none", MonitoringStatus: schema.MonitoringStatusRunningOrArchiving, } if err = dec.Decode(&job); err != nil { diff --git a/internal/importer/importer_test.go b/internal/importer/importer_test.go index 11e7afe..ff28619 100644 --- a/internal/importer/importer_test.go +++ b/internal/importer/importer_test.go @@ -16,6 +16,7 @@ import ( "github.com/ClusterCockpit/cc-backend/internal/importer" "github.com/ClusterCockpit/cc-backend/internal/repository" "github.com/ClusterCockpit/cc-backend/pkg/archive" + ccconf "github.com/ClusterCockpit/cc-lib/ccConfig" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" ) @@ -36,18 +37,16 @@ func copyFile(s string, d string) error { func setup(t *testing.T) *repository.JobRepository { const testconfig = `{ + "main": { "addr": "0.0.0.0:8080", "validate": false, + "apiAllowedIPs": [ + "*" + ]}, "archive": { "kind": "file", "path": "./var/job-archive" }, - "jwts": { - "max-age": "2m" - }, - "apiAllowedIPs": [ - "*" - ], "clusters": [ { "name": "testcluster", @@ -108,7 +107,19 @@ func setup(t *testing.T) *repository.JobRepository { t.Fatal(err) } - config.Init(cfgFilePath) + ccconf.Init(cfgFilePath) + + // Load and check main configuration + if cfg := ccconf.GetPackageConfig("main"); cfg != nil { + if clustercfg := ccconf.GetPackageConfig("clusters"); clustercfg != nil { + config.Init(cfg, clustercfg) + } else { + t.Fatal("Cluster configuration must be present") + } + } else { + t.Fatal("Main configuration must be present") + } + archiveCfg := fmt.Sprintf("{\"kind\": \"file\",\"path\": \"%s\"}", jobarchive) if err := archive.Init(json.RawMessage(archiveCfg), config.Keys.DisableArchive); err != nil { diff --git a/internal/metricDataDispatcher/dataLoader.go b/internal/metricDataDispatcher/dataLoader.go index 6307843..2b73e11 100644 --- a/internal/metricDataDispatcher/dataLoader.go +++ b/internal/metricDataDispatcher/dataLoader.go @@ -41,7 +41,7 @@ func LoadData(job *schema.Job, ctx context.Context, resolution int, ) (schema.JobData, error) { - data := cache.Get(cacheKey(job, metrics, scopes, resolution), func() (_ interface{}, ttl time.Duration, size int) { + data := cache.Get(cacheKey(job, metrics, scopes, resolution), func() (_ any, ttl time.Duration, size int) { var jd schema.JobData var err error diff --git a/internal/metricdata/metricdata.go b/internal/metricdata/metricdata.go index aa3a87c..87867af 100644 --- a/internal/metricdata/metricdata.go +++ b/internal/metricdata/metricdata.go @@ -40,7 +40,7 @@ type MetricDataRepository interface { var metricDataRepos map[string]MetricDataRepository = map[string]MetricDataRepository{} func Init() error { - for _, cluster := range config.Keys.Clusters { + for _, cluster := range config.Clusters { if cluster.MetricDataRepository != nil { var kind struct { Kind string `json:"kind"` diff --git a/internal/repository/job.go b/internal/repository/job.go index 2cde824..957c3b7 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -74,7 +74,7 @@ func scanJob(row interface{ Scan(...any) error }) (*schema.Job, error) { if err := row.Scan( &job.ID, &job.JobID, &job.User, &job.Project, &job.Cluster, &job.SubCluster, &job.StartTime, &job.Partition, &job.ArrayJobId, &job.NumNodes, &job.NumHWThreads, - &job.NumAcc, &job.Exclusive, &job.MonitoringStatus, &job.SMT, &job.State, + &job.NumAcc, &job.Shared, &job.MonitoringStatus, &job.SMT, &job.State, &job.Duration, &job.Walltime, &job.RawResources, &job.RawFootprint, &job.Energy); err != nil { cclog.Warnf("Error while scanning rows (Job): %v", err) return nil, err diff --git a/internal/repository/jobQuery.go b/internal/repository/jobQuery.go index c9ccb03..fdcc904 100644 --- a/internal/repository/jobQuery.go +++ b/internal/repository/jobQuery.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/graph/model" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" @@ -216,7 +217,7 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select return query } -func buildIntCondition(field string, cond *schema.IntRange, query sq.SelectBuilder) sq.SelectBuilder { +func buildIntCondition(field string, cond *config.IntRange, query sq.SelectBuilder) sq.SelectBuilder { return query.Where(field+" BETWEEN ? AND ?", cond.From, cond.To) } @@ -224,7 +225,7 @@ func buildFloatCondition(field string, cond *model.FloatRange, query sq.SelectBu return query.Where(field+" BETWEEN ? AND ?", cond.From, cond.To) } -func buildTimeCondition(field string, cond *schema.TimeRange, query sq.SelectBuilder) sq.SelectBuilder { +func buildTimeCondition(field string, cond *config.TimeRange, query sq.SelectBuilder) sq.SelectBuilder { if cond.From != nil && cond.To != nil { return query.Where(field+" BETWEEN ? AND ?", cond.From.Unix(), cond.To.Unix()) } else if cond.From != nil { diff --git a/internal/repository/migrations/sqlite3/09_add-job-cache.up.sql b/internal/repository/migrations/sqlite3/09_add-job-cache.up.sql index 7840369..003eab0 100644 --- a/internal/repository/migrations/sqlite3/09_add-job-cache.up.sql +++ b/internal/repository/migrations/sqlite3/09_add-job-cache.up.sql @@ -1,9 +1,10 @@ CREATE TABLE "job_cache" ( id INTEGER PRIMARY KEY, job_id BIGINT NOT NULL, - cluster VARCHAR(255) NOT NULL, + hpc_cluster VARCHAR(255) NOT NULL, subcluster VARCHAR(255) NOT NULL, - start_time BIGINT NOT NULL, -- Unix timestamp + submit_time BIGINT NOT NULL, -- Unix timestamp + start_time BIGINT NOT NULL DEFAULT 0, -- Unix timestamp hpc_user VARCHAR(255) NOT NULL, project VARCHAR(255) NOT NULL, cluster_partition VARCHAR(255), @@ -12,8 +13,9 @@ CREATE TABLE "job_cache" ( walltime INT NOT NULL, job_state VARCHAR(255) NOT NULL CHECK (job_state IN ( - 'running', 'completed', 'failed', 'cancelled', - 'stopped', 'timeout', 'preempted', 'out_of_memory' + 'boot_fail', 'cancelled', 'completed', 'deadline', + 'failed', 'node_fail', 'out-of-memory', 'pending', + 'preempted', 'running', 'suspended', 'timeout' )), meta_data TEXT, -- JSON resources TEXT NOT NULL, -- JSON @@ -21,7 +23,8 @@ CREATE TABLE "job_cache" ( num_hwthreads INT, num_acc INT, smt TINYINT NOT NULL DEFAULT 1 CHECK (smt IN (0, 1)), - exclusive TINYINT NOT NULL DEFAULT 1 CHECK (exclusive IN (0, 1, 2)), + shared TEXT NOT NULL + CHECK (shared IN ("none", "single_user", "multi_user")), monitoring_status TINYINT NOT NULL DEFAULT 1 CHECK (monitoring_status IN (0, 1, 2, 3)), energy REAL NOT NULL DEFAULT 0.0, @@ -29,3 +32,43 @@ CREATE TABLE "job_cache" ( footprint TEXT DEFAULT NULL, UNIQUE (job_id, cluster, start_time) ); + +CREATE TABLE "job_new" ( + id INTEGER PRIMARY KEY, + job_id BIGINT NOT NULL, + hpc_cluster TEXT NOT NULL, + subcluster TEXT NOT NULL, + submit_time BIGINT NOT NULL DEFAULT 0, -- Unix timestamp + start_time BIGINT NOT NULL DEFAULT 0, -- Unix timestamp + hpc_user TEXT NOT NULL, + project TEXT NOT NULL, + cluster_partition TEXT, + array_job_id BIGINT, + duration INT NOT NULL, + walltime INT NOT NULL, + job_state TEXT NOT NULL + CHECK (job_state IN ( + 'boot_fail', 'cancelled', 'completed', 'deadline', + 'failed', 'node_fail', 'out-of-memory', 'pending', + 'preempted', 'running', 'suspended', 'timeout' + )), + meta_data TEXT, -- JSON + resources TEXT NOT NULL, -- JSON + num_nodes INT NOT NULL, + num_hwthreads INT, + num_acc INT, + smt INT NOT NULL DEFAULT 1, + shared TEXT NOT NULL + CHECK (shared IN ("none", "single_user", "multi_user")), + monitoring_status TINYINT NOT NULL DEFAULT 1 + CHECK (monitoring_status IN (0, 1, 2, 3)), + energy REAL NOT NULL DEFAULT 0.0, + energy_footprint TEXT DEFAULT NULL, + footprint TEXT DEFAULT NULL, + UNIQUE (job_id, cluster, start_time) +); + +ALTER TABLE job RENAME COLUMN cluster TO hpc_cluster; +INSERT INTO job_new SELECT * FROM job; +DROP TABLE job; +ALTER TABLE job_new RENAME TO job; diff --git a/internal/repository/migrations/sqlite3/10_node-table.up.sql b/internal/repository/migrations/sqlite3/10_node-table.up.sql index 1211ba9..03a9bf6 100644 --- a/internal/repository/migrations/sqlite3/10_node-table.up.sql +++ b/internal/repository/migrations/sqlite3/10_node-table.up.sql @@ -1,5 +1,6 @@ CREATE TABLE "node" ( id INTEGER PRIMARY KEY, + time_stamp INTEGER NOT NULL, hostname VARCHAR(255) NOT NULL, cluster VARCHAR(255) NOT NULL, subcluster VARCHAR(255) NOT NULL, @@ -33,4 +34,4 @@ CREATE INDEX IF NOT EXISTS nodes_cluster_health ON node (cluster, health_state); -- Add Indices For Increased Amounts of Tags CREATE INDEX IF NOT EXISTS tags_jobid ON jobtag (job_id); -CREATE INDEX IF NOT EXISTS tags_tagid ON jobtag (tag_id); \ No newline at end of file +CREATE INDEX IF NOT EXISTS tags_tagid ON jobtag (tag_id); diff --git a/internal/repository/userConfig.go b/internal/repository/userConfig.go index 2ef7164..83f3140 100644 --- a/internal/repository/userConfig.go +++ b/internal/repository/userConfig.go @@ -6,6 +6,7 @@ package repository import ( "encoding/json" + "maps" "sync" "time" @@ -24,7 +25,7 @@ var ( type UserCfgRepo struct { DB *sqlx.DB Lookup *sqlx.Stmt - uiDefaults map[string]interface{} + uiDefaults map[string]any cache *lrucache.Cache lock sync.RWMutex } @@ -51,22 +52,18 @@ func GetUserCfgRepo() *UserCfgRepo { // Return the personalised UI config for the currently authenticated // user or return the plain default config. -func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]interface{}, error) { +func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]any, error) { if user == nil { uCfg.lock.RLock() - copy := make(map[string]interface{}, len(uCfg.uiDefaults)) - for k, v := range uCfg.uiDefaults { - copy[k] = v - } + copy := make(map[string]any, len(uCfg.uiDefaults)) + maps.Copy(copy, uCfg.uiDefaults) uCfg.lock.RUnlock() return copy, nil } - data := uCfg.cache.Get(user.Username, func() (interface{}, time.Duration, int) { - uiconfig := make(map[string]interface{}, len(uCfg.uiDefaults)) - for k, v := range uCfg.uiDefaults { - uiconfig[k] = v - } + data := uCfg.cache.Get(user.Username, func() (any, time.Duration, int) { + uiconfig := make(map[string]any, len(uCfg.uiDefaults)) + maps.Copy(uiconfig, uCfg.uiDefaults) rows, err := uCfg.Lookup.Query(user.Username) if err != nil { @@ -83,7 +80,7 @@ func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]interface{}, return err, 0, 0 } - var val interface{} + var val any if err := json.Unmarshal([]byte(rawval), &val); err != nil { cclog.Warn("Error while unmarshaling raw user uiconfig json") return err, 0, 0 @@ -104,7 +101,7 @@ func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]interface{}, return nil, err } - return data.(map[string]interface{}), nil + return data.(map[string]any), nil } // If the context does not have a user, update the global ui configuration @@ -115,7 +112,7 @@ func (uCfg *UserCfgRepo) UpdateConfig( user *schema.User, ) error { if user == nil { - var val interface{} + var val any if err := json.Unmarshal([]byte(value), &val); err != nil { cclog.Warn("Error while unmarshaling raw user config json") return err diff --git a/internal/repository/userConfig_test.go b/internal/repository/userConfig_test.go index d200763..b8cfd5e 100644 --- a/internal/repository/userConfig_test.go +++ b/internal/repository/userConfig_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/ClusterCockpit/cc-backend/internal/config" + ccconf "github.com/ClusterCockpit/cc-lib/ccConfig" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" _ "github.com/mattn/go-sqlite3" @@ -17,17 +18,16 @@ import ( func setupUserTest(t *testing.T) *UserCfgRepo { const testconfig = `{ - "addr": "0.0.0.0:8080", + "main": { + "addr": "0.0.0.0:8080", + "apiAllowedIPs": [ + "*" + ] + }, "archive": { "kind": "file", "path": "./var/job-archive" }, - "jwts": { - "max-age": "2m" - }, - "apiAllowedIPs": [ - "*" - ], "clusters": [ { "name": "testcluster", @@ -36,7 +36,8 @@ func setupUserTest(t *testing.T) *UserCfgRepo { "numNodes": { "from": 1, "to": 64 }, "duration": { "from": 0, "to": 86400 }, "startTime": { "from": "2022-01-01T00:00:00Z", "to": null } - } } ] + } + }] }` cclog.Init("info", true) @@ -53,7 +54,19 @@ func setupUserTest(t *testing.T) *UserCfgRepo { t.Fatal(err) } - config.Init(cfgFilePath) + ccconf.Init(cfgFilePath) + + // Load and check main configuration + if cfg := ccconf.GetPackageConfig("main"); cfg != nil { + if clustercfg := ccconf.GetPackageConfig("clusters"); clustercfg != nil { + config.Init(cfg, clustercfg) + } else { + t.Fatal("Cluster configuration must be present") + } + } else { + t.Fatal("Main configuration must be present") + } + return GetUserCfgRepo() } diff --git a/internal/tagger/classifyJob.go b/internal/tagger/classifyJob.go index 32063cd..0317f81 100644 --- a/internal/tagger/classifyJob.go +++ b/internal/tagger/classifyJob.go @@ -240,13 +240,13 @@ func (t *JobClassTagger) Match(job *schema.Job) { // Initialize environment env["job"] = map[string]any{ - "exclusive": job.Exclusive, - "duration": job.Duration, - "numCores": job.NumHWThreads, - "numNodes": job.NumNodes, - "jobState": job.State, - "numAcc": job.NumAcc, - "smt": job.SMT, + "shared": job.Shared, + "duration": job.Duration, + "numCores": job.NumHWThreads, + "numNodes": job.NumNodes, + "jobState": job.State, + "numAcc": job.NumAcc, + "smt": job.SMT, } // add metrics to env diff --git a/internal/taskManager/commitJobService.go b/internal/taskManager/commitJobService.go index 5489007..e7c169a 100644 --- a/internal/taskManager/commitJobService.go +++ b/internal/taskManager/commitJobService.go @@ -7,7 +7,6 @@ package taskManager import ( "time" - "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/repository" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/go-co-op/gocron/v2" @@ -15,8 +14,8 @@ import ( func RegisterCommitJobService() { var frequency string - if config.Keys.CronFrequency != nil && config.Keys.CronFrequency.CommitJobWorker != "" { - frequency = config.Keys.CronFrequency.CommitJobWorker + if Keys.CommitJobWorker != "" { + frequency = Keys.CommitJobWorker } else { frequency = "2m" } diff --git a/internal/taskManager/taskManager.go b/internal/taskManager/taskManager.go index 5f51040..7231d12 100644 --- a/internal/taskManager/taskManager.go +++ b/internal/taskManager/taskManager.go @@ -5,19 +5,37 @@ package taskManager import ( + "bytes" "encoding/json" "time" + "github.com/ClusterCockpit/cc-backend/internal/auth" "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/repository" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" - "github.com/ClusterCockpit/cc-lib/schema" "github.com/go-co-op/gocron/v2" ) +type Retention struct { + Policy string `json:"policy"` + Location string `json:"location"` + Age int `json:"age"` + IncludeDB bool `json:"includeDB"` +} + +type CronFrequency struct { + // Duration Update Worker [Defaults to '2m'] + CommitJobWorker string `json:"commit-job-worker"` + // Duration Update Worker [Defaults to '5m'] + DurationWorker string `json:"duration-worker"` + // Metric-Footprint Update Worker [Defaults to '10m'] + FootprintWorker string `json:"footprint-worker"` +} + var ( s gocron.Scheduler jobRepo *repository.JobRepository + Keys CronFrequency ) func parseDuration(s string) (time.Duration, error) { @@ -35,7 +53,7 @@ func parseDuration(s string) (time.Duration, error) { return interval, nil } -func Start() { +func Start(cronCfg, archiveConfig json.RawMessage) { var err error jobRepo = repository.GetJobRepository() s, err = gocron.NewScheduler() @@ -47,13 +65,19 @@ func Start() { RegisterStopJobsExceedTime() } + dec := json.NewDecoder(bytes.NewReader(cronCfg)) + dec.DisallowUnknownFields() + if err := dec.Decode(&Keys); err != nil { + cclog.Errorf("error while decoding ldap config: %v", err) + } + var cfg struct { - Retention schema.Retention `json:"retention"` - Compression int `json:"compression"` + Retention Retention `json:"retention"` + Compression int `json:"compression"` } cfg.Retention.IncludeDB = true - if err := json.Unmarshal(config.Keys.Archive, &cfg); err != nil { + if err := json.Unmarshal(archiveConfig, &cfg); err != nil { cclog.Warn("Error while unmarshaling raw config json") } @@ -73,7 +97,7 @@ func Start() { RegisterCompressionService(cfg.Compression) } - lc := config.Keys.LdapConfig + lc := auth.Keys.LdapConfig if lc != nil && lc.SyncInterval != "" { RegisterLdapSyncService(lc.SyncInterval) diff --git a/internal/taskManager/updateDurationService.go b/internal/taskManager/updateDurationService.go index 70ec506..d650afb 100644 --- a/internal/taskManager/updateDurationService.go +++ b/internal/taskManager/updateDurationService.go @@ -7,15 +7,14 @@ package taskManager import ( "time" - "github.com/ClusterCockpit/cc-backend/internal/config" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/go-co-op/gocron/v2" ) func RegisterUpdateDurationWorker() { var frequency string - if config.Keys.CronFrequency != nil && config.Keys.CronFrequency.DurationWorker != "" { - frequency = config.Keys.CronFrequency.DurationWorker + if Keys.DurationWorker != "" { + frequency = Keys.DurationWorker } else { frequency = "5m" } diff --git a/internal/taskManager/updateFootprintService.go b/internal/taskManager/updateFootprintService.go index 41c5837..4025849 100644 --- a/internal/taskManager/updateFootprintService.go +++ b/internal/taskManager/updateFootprintService.go @@ -9,7 +9,6 @@ import ( "math" "time" - "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/internal/metricdata" "github.com/ClusterCockpit/cc-backend/pkg/archive" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" @@ -20,8 +19,8 @@ import ( func RegisterFootprintWorker() { var frequency string - if config.Keys.CronFrequency != nil && config.Keys.CronFrequency.FootprintWorker != "" { - frequency = config.Keys.CronFrequency.FootprintWorker + if Keys.FootprintWorker != "" { + frequency = Keys.FootprintWorker } else { frequency = "10m" } diff --git a/pkg/archive/ConfigSchema.go b/pkg/archive/ConfigSchema.go new file mode 100644 index 0000000..d4721f6 --- /dev/null +++ b/pkg/archive/ConfigSchema.go @@ -0,0 +1,49 @@ +// 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 archive + +var configSchema = ` + { + "type": "object", + "properties": { + "kind": { + "description": "Backend type for job-archive", + "type": "string", + "enum": ["file", "s3"] + }, + "path": { + "description": "Path to job archive for file backend", + "type": "string" + }, + "compression": { + "description": "Setup automatic compression for jobs older than number of days", + "type": "integer" + }, + "retention": { + "description": "Configuration keys for retention", + "type": "object", + "properties": { + "policy": { + "description": "Retention policy", + "type": "string", + "enum": ["none", "delete", "move"] + }, + "includeDB": { + "description": "Also remove jobs from database", + "type": "boolean" + }, + "age": { + "description": "Act on jobs with startTime older than age (in days)", + "type": "integer" + }, + "location": { + "description": "The target directory for retention. Only applicable for retention move.", + "type": "string" + } + }, + "required": ["policy"] + } + }, + "required": ["kind"]}` diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index f69cde3..db6f1f8 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -10,6 +10,7 @@ import ( "maps" "sync" + "github.com/ClusterCockpit/cc-backend/internal/config" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/lrucache" "github.com/ClusterCockpit/cc-lib/schema" @@ -74,6 +75,7 @@ func Init(rawConfig json.RawMessage, disableArchive bool) error { Kind string `json:"kind"` } + config.Validate(configSchema, rawConfig) if err = json.Unmarshal(rawConfig, &cfg); err != nil { cclog.Warn("Error while unmarshaling raw config json") return diff --git a/pkg/archive/fsBackend.go b/pkg/archive/fsBackend.go index 8f10360..53a374f 100644 --- a/pkg/archive/fsBackend.go +++ b/pkg/archive/fsBackend.go @@ -147,17 +147,17 @@ func loadJobStats(filename string, isCompressed bool) (schema.ScopedJobStats, er } func (fsa *FsArchive) Init(rawConfig json.RawMessage) (uint64, error) { - var config FsArchiveConfig - if err := json.Unmarshal(rawConfig, &config); err != nil { + var cfg FsArchiveConfig + if err := json.Unmarshal(rawConfig, &cfg); err != nil { cclog.Warnf("Init() > Unmarshal error: %#v", err) return 0, err } - if config.Path == "" { + if cfg.Path == "" { err := fmt.Errorf("Init() : empty config.Path") cclog.Errorf("Init() > config.Path error: %v", err) return 0, err } - fsa.path = config.Path + fsa.path = cfg.Path b, err := os.ReadFile(filepath.Join(fsa.path, "version.txt")) if err != nil { diff --git a/pkg/archive/fsBackend_test.go b/pkg/archive/fsBackend_test.go index 7b1fe74..c872d0a 100644 --- a/pkg/archive/fsBackend_test.go +++ b/pkg/archive/fsBackend_test.go @@ -1,5 +1,5 @@ // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. -// All rights reserved. This file is part of cc-backend. +// All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package archive @@ -86,7 +86,7 @@ func TestLoadJobMeta(t *testing.T) { } jobIn := schema.Job{ - Exclusive: 1, + Shared: "none", MonitoringStatus: schema.MonitoringStatusRunningOrArchiving, } jobIn.StartTime = 1608923076 @@ -117,7 +117,7 @@ func TestLoadJobData(t *testing.T) { } jobIn := schema.Job{ - Exclusive: 1, + Shared: "none", MonitoringStatus: schema.MonitoringStatusRunningOrArchiving, } jobIn.StartTime = 1608923076 @@ -148,7 +148,7 @@ func BenchmarkLoadJobData(b *testing.B) { fsa.Init(json.RawMessage(archiveCfg)) jobIn := schema.Job{ - Exclusive: 1, + Shared: "none", MonitoringStatus: schema.MonitoringStatusRunningOrArchiving, } jobIn.StartTime = 1608923076 @@ -174,7 +174,7 @@ func BenchmarkLoadJobDataCompressed(b *testing.B) { fsa.Init(json.RawMessage(archiveCfg)) jobIn := schema.Job{ - Exclusive: 1, + Shared: "none", MonitoringStatus: schema.MonitoringStatusRunningOrArchiving, } jobIn.StartTime = 1608923076 diff --git a/pkg/runtimeEnv/setup.go b/pkg/runtimeEnv/setup.go deleted file mode 100644 index e23a004..0000000 --- a/pkg/runtimeEnv/setup.go +++ /dev/null @@ -1,73 +0,0 @@ -// 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 runtimeEnv - -import ( - "fmt" - "os" - "os/exec" - "os/user" - "strconv" - "syscall" - - cclog "github.com/ClusterCockpit/cc-lib/ccLogger" -) - -// Changes the processes user and group to that -// specified in the config.json. The go runtime -// takes care of all threads (and not only the calling one) -// executing the underlying systemcall. -func DropPrivileges(username string, group string) error { - if group != "" { - g, err := user.LookupGroup(group) - if err != nil { - cclog.Warn("Error while looking up group") - return err - } - - gid, _ := strconv.Atoi(g.Gid) - if err := syscall.Setgid(gid); err != nil { - cclog.Warn("Error while setting gid") - return err - } - } - - if username != "" { - u, err := user.Lookup(username) - if err != nil { - cclog.Warn("Error while looking up user") - return err - } - - uid, _ := strconv.Atoi(u.Uid) - if err := syscall.Setuid(uid); err != nil { - cclog.Warn("Error while setting uid") - return err - } - } - - return nil -} - -// If started via systemd, inform systemd that we are running: -// https://www.freedesktop.org/software/systemd/man/sd_notify.html -func SystemdNotifiy(ready bool, status string) { - if os.Getenv("NOTIFY_SOCKET") == "" { - // Not started using systemd - return - } - - args := []string{fmt.Sprintf("--pid=%d", os.Getpid())} - if ready { - args = append(args, "--ready") - } - - if status != "" { - args = append(args, fmt.Sprintf("--status=%s", status)) - } - - cmd := exec.Command("systemd-notify", args...) - cmd.Run() // errors ignored on purpose, there is not much to do anyways. -} diff --git a/tools/archive-manager/main.go b/tools/archive-manager/main.go index 0cf5f98..5073d5d 100644 --- a/tools/archive-manager/main.go +++ b/tools/archive-manager/main.go @@ -13,6 +13,7 @@ import ( "github.com/ClusterCockpit/cc-backend/internal/config" "github.com/ClusterCockpit/cc-backend/pkg/archive" + ccconf "github.com/ClusterCockpit/cc-lib/ccConfig" cclog "github.com/ClusterCockpit/cc-lib/ccLogger" ) @@ -47,7 +48,19 @@ func main() { archiveCfg := fmt.Sprintf("{\"kind\": \"file\",\"path\": \"%s\"}", srcPath) cclog.Init(flagLogLevel, flagLogDateTime) - config.Init(flagConfigFile) + + ccconf.Init(flagConfigFile) + + // Load and check main configuration + if cfg := ccconf.GetPackageConfig("main"); cfg != nil { + if clustercfg := ccconf.GetPackageConfig("clusters"); clustercfg != nil { + config.Init(cfg, clustercfg) + } else { + cclog.Abort("Cluster configuration must be present") + } + } else { + cclog.Abort("Main configuration must be present") + } if err := archive.Init(json.RawMessage(archiveCfg), false); err != nil { cclog.Fatal(err) diff --git a/web/web.go b/web/web.go index 7318284..0c8c160 100644 --- a/web/web.go +++ b/web/web.go @@ -90,12 +90,12 @@ type Page struct { User schema.User // Information about the currently logged in user (Full User Info) Roles map[string]schema.Role // Available roles for frontend render checks Build Build // Latest information about the application - Clusters []schema.ClusterConfig // List of all clusters for use in the Header + Clusters []config.ClusterConfig // List of all clusters for use in the Header SubClusters map[string][]string // Map per cluster of all subClusters for use in the Header FilterPresets map[string]interface{} // For pages with the Filter component, this can be used to set initial filters. Infos map[string]interface{} // For generic use (e.g. username for /monitoring/user/, job id for /monitoring/job/) Config map[string]interface{} // UI settings for the currently logged in user (e.g. line width, ...) - Resampling *schema.ResampleConfig // If not nil, defines resampling trigger and resolutions + Resampling *config.ResampleConfig // If not nil, defines resampling trigger and resolutions Redirect string // The originally requested URL, for intermediate login handling } @@ -106,8 +106,8 @@ func RenderTemplate(rw http.ResponseWriter, file string, page *Page) { } if page.Clusters == nil { - for _, c := range config.Keys.Clusters { - page.Clusters = append(page.Clusters, schema.ClusterConfig{Name: c.Name, FilterRanges: c.FilterRanges, MetricDataRepository: nil}) + for _, c := range config.Clusters { + page.Clusters = append(page.Clusters, config.ClusterConfig{Name: c.Name, FilterRanges: c.FilterRanges, MetricDataRepository: nil}) } }