mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-08-02 17:30:36 +02:00
Compare commits
11 Commits
ccfront-de
...
nats-clien
Author | SHA1 | Date | |
---|---|---|---|
18376aa64b | |||
a5fdccc764 | |||
|
cbc49669d0 | ||
|
78bb638fd6 | ||
|
7a61bae471 | ||
|
e1b992526e | ||
|
b6b37ee68b | ||
|
43cb1f1bff | ||
|
f158eaa29c | ||
|
86d85f12be | ||
|
6247150e9c |
22
.gitignore
vendored
22
.gitignore
vendored
@@ -1,23 +1,21 @@
|
|||||||
/cc-backend
|
/cc-backend
|
||||||
|
|
||||||
|
/var/job-archive
|
||||||
|
/var/*.db
|
||||||
|
/var/machine-state
|
||||||
|
|
||||||
/.env
|
/.env
|
||||||
/config.json
|
/config.json
|
||||||
|
|
||||||
/var/job-archive
|
|
||||||
/var/machine-state
|
|
||||||
/var/job.db-shm
|
|
||||||
/var/job.db-wal
|
|
||||||
/var/*.db
|
|
||||||
/var/*.txt
|
|
||||||
|
|
||||||
/web/frontend/public/build
|
/web/frontend/public/build
|
||||||
/web/frontend/node_modules
|
/web/frontend/node_modules
|
||||||
|
/.vscode/*
|
||||||
/archive-migration
|
/archive-migration
|
||||||
/archive-manager
|
/archive-manager
|
||||||
|
var/job.db-shm
|
||||||
|
var/job.db-wal
|
||||||
|
|
||||||
/internal/repository/testdata/job.db-shm
|
|
||||||
/internal/repository/testdata/job.db-wal
|
|
||||||
|
|
||||||
/.vscode/*
|
|
||||||
dist/
|
dist/
|
||||||
*.db
|
*.db
|
||||||
|
internal/repository/testdata/job.db-shm
|
||||||
|
internal/repository/testdata/job.db-wal
|
||||||
|
2
Makefile
2
Makefile
@@ -82,7 +82,7 @@ tags:
|
|||||||
@ctags -R
|
@ctags -R
|
||||||
|
|
||||||
$(VAR):
|
$(VAR):
|
||||||
@mkdir -p $(VAR)
|
@mkdir $(VAR)
|
||||||
|
|
||||||
config.json:
|
config.json:
|
||||||
$(info ===> Initialize config.json file)
|
$(info ===> Initialize config.json file)
|
||||||
|
100
api/swagger.json
100
api/swagger.json
@@ -202,7 +202,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Success message",
|
"description": "Success message",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.DeleteJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -272,7 +272,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Success message",
|
"description": "Success message",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.DeleteJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -342,7 +342,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Success message",
|
"description": "Success message",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.DeleteJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -487,7 +487,7 @@
|
|||||||
"201": {
|
"201": {
|
||||||
"description": "Job added successfully",
|
"description": "Job added successfully",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.StartJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -581,7 +581,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"422": {
|
"422": {
|
||||||
"description": "Unprocessable Entity: job has already been stopped",
|
"description": "Unprocessable Entity: finding job failed: sql: no rows in result set",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.ErrorResponse"
|
"$ref": "#/definitions/api.ErrorResponse"
|
||||||
}
|
}
|
||||||
@@ -827,72 +827,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/notice/": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Modifies the content of notice.txt, shown as notice box on the homepage.\nIf more than one formValue is set then only the highest priority field is used.\nOnly accessible from IPs registered with apiAllowedIPs configuration option.",
|
|
||||||
"consumes": [
|
|
||||||
"multipart/form-data"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"text/plain"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"User"
|
|
||||||
],
|
|
||||||
"summary": "Updates or empties the notice box content",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Priority 1: New content to display",
|
|
||||||
"name": "new-content",
|
|
||||||
"in": "formData"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Success Response Message",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"422": {
|
|
||||||
"description": "Unprocessable Entity: The user could not be updated",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/user/{id}": {
|
"/user/{id}": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -1273,14 +1207,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api.DefaultJobApiResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"msg": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"api.DeleteJobApiRequest": {
|
"api.DeleteJobApiRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@@ -1304,6 +1230,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.DeleteJobApiResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.EditMetaRequest": {
|
"api.EditMetaRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -1390,6 +1324,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.StartJobApiResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.StopJobApiRequest": {
|
"api.StopJobApiRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
@@ -32,11 +32,6 @@ definitions:
|
|||||||
example: Debug
|
example: Debug
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
api.DefaultJobApiResponse:
|
|
||||||
properties:
|
|
||||||
msg:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
api.DeleteJobApiRequest:
|
api.DeleteJobApiRequest:
|
||||||
properties:
|
properties:
|
||||||
cluster:
|
cluster:
|
||||||
@@ -54,6 +49,11 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- jobId
|
- jobId
|
||||||
type: object
|
type: object
|
||||||
|
api.DeleteJobApiResponse:
|
||||||
|
properties:
|
||||||
|
msg:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
api.EditMetaRequest:
|
api.EditMetaRequest:
|
||||||
properties:
|
properties:
|
||||||
key:
|
key:
|
||||||
@@ -112,6 +112,11 @@ definitions:
|
|||||||
scope:
|
scope:
|
||||||
$ref: '#/definitions/schema.MetricScope'
|
$ref: '#/definitions/schema.MetricScope'
|
||||||
type: object
|
type: object
|
||||||
|
api.StartJobApiResponse:
|
||||||
|
properties:
|
||||||
|
msg:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
api.StopJobApiRequest:
|
api.StopJobApiRequest:
|
||||||
properties:
|
properties:
|
||||||
cluster:
|
cluster:
|
||||||
@@ -901,7 +906,7 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: Success message
|
description: Success message
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/api.DefaultJobApiResponse'
|
$ref: '#/definitions/api.DeleteJobApiResponse'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -948,7 +953,7 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: Success message
|
description: Success message
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/api.DefaultJobApiResponse'
|
$ref: '#/definitions/api.DeleteJobApiResponse'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -995,7 +1000,7 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: Success message
|
description: Success message
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/api.DefaultJobApiResponse'
|
$ref: '#/definitions/api.DeleteJobApiResponse'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -1093,7 +1098,7 @@ paths:
|
|||||||
"201":
|
"201":
|
||||||
description: Job added successfully
|
description: Job added successfully
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/api.DefaultJobApiResponse'
|
$ref: '#/definitions/api.StartJobApiResponse'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -1156,7 +1161,8 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/api.ErrorResponse'
|
$ref: '#/definitions/api.ErrorResponse'
|
||||||
"422":
|
"422":
|
||||||
description: 'Unprocessable Entity: job has already been stopped'
|
description: 'Unprocessable Entity: finding job failed: sql: no rows in
|
||||||
|
result set'
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/api.ErrorResponse'
|
$ref: '#/definitions/api.ErrorResponse'
|
||||||
"500":
|
"500":
|
||||||
@@ -1218,51 +1224,6 @@ paths:
|
|||||||
summary: Adds one or more tags to a job
|
summary: Adds one or more tags to a job
|
||||||
tags:
|
tags:
|
||||||
- Job add and modify
|
- Job add and modify
|
||||||
/notice/:
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- multipart/form-data
|
|
||||||
description: |-
|
|
||||||
Modifies the content of notice.txt, shown as notice box on the homepage.
|
|
||||||
If more than one formValue is set then only the highest priority field is used.
|
|
||||||
Only accessible from IPs registered with apiAllowedIPs configuration option.
|
|
||||||
parameters:
|
|
||||||
- description: 'Priority 1: New content to display'
|
|
||||||
in: formData
|
|
||||||
name: new-content
|
|
||||||
type: string
|
|
||||||
produces:
|
|
||||||
- text/plain
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Success Response Message
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
"400":
|
|
||||||
description: Bad Request
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
"401":
|
|
||||||
description: Unauthorized
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
"403":
|
|
||||||
description: Forbidden
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
"422":
|
|
||||||
description: 'Unprocessable Entity: The user could not be updated'
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
security:
|
|
||||||
- ApiKeyAuth: []
|
|
||||||
summary: Updates or empties the notice box content
|
|
||||||
tags:
|
|
||||||
- User
|
|
||||||
/user/{id}:
|
/user/{id}:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
12
configs/default_metrics.json
Normal file
12
configs/default_metrics.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"clusters": [
|
||||||
|
{
|
||||||
|
"name": "fritz",
|
||||||
|
"default_metrics": "cpu_load, flops_any, core_power, lustre_open, mem_used, mem_bw, net_bytes_in"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "alex",
|
||||||
|
"default_metrics": "flops_any, mem_bw, mem_used, vectorization_ratio"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
28
go.mod
28
go.mod
@@ -4,6 +4,7 @@ go 1.23.5
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.17.63
|
github.com/99designs/gqlgen v0.17.63
|
||||||
|
github.com/ClusterCockpit/cc-lib v0.0.0-20250216162506-b737e48423c5
|
||||||
github.com/ClusterCockpit/cc-units v0.4.0
|
github.com/ClusterCockpit/cc-units v0.4.0
|
||||||
github.com/Masterminds/squirrel v1.5.4
|
github.com/Masterminds/squirrel v1.5.4
|
||||||
github.com/coreos/go-oidc/v3 v3.11.0
|
github.com/coreos/go-oidc/v3 v3.11.0
|
||||||
@@ -16,19 +17,20 @@ require (
|
|||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/gorilla/sessions v1.4.0
|
github.com/gorilla/sessions v1.4.0
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.13.0
|
github.com/influxdata/influxdb-client-go/v2 v2.14.0
|
||||||
github.com/jmoiron/sqlx v1.4.0
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
github.com/prometheus/client_golang v1.19.1
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/prometheus/common v0.55.0
|
github.com/prometheus/common v0.55.0
|
||||||
github.com/qustavo/sqlhooks/v2 v2.1.0
|
github.com/qustavo/sqlhooks/v2 v2.1.0
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||||
github.com/swaggo/http-swagger v1.3.4
|
github.com/swaggo/http-swagger v1.3.4
|
||||||
github.com/swaggo/swag v1.16.4
|
github.com/swaggo/swag v1.16.4
|
||||||
github.com/vektah/gqlparser/v2 v2.5.22
|
github.com/vektah/gqlparser/v2 v2.5.22
|
||||||
golang.org/x/crypto v0.32.0
|
golang.org/x/crypto v0.33.0
|
||||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37
|
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f
|
||||||
golang.org/x/oauth2 v0.21.0
|
golang.org/x/oauth2 v0.21.0
|
||||||
|
golang.org/x/time v0.10.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -40,6 +42,7 @@ require (
|
|||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // 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.6 // indirect
|
||||||
|
github.com/expr-lang/expr v1.16.9 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.3 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.3 // indirect
|
||||||
@@ -55,10 +58,12 @@ require (
|
|||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
|
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
|
||||||
|
github.com/influxdata/line-protocol/v2 v2.2.1 // indirect
|
||||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/jpillora/backoff v1.0.0 // indirect
|
github.com/jpillora/backoff v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
github.com/mailru/easyjson v0.9.0 // indirect
|
github.com/mailru/easyjson v0.9.0 // indirect
|
||||||
@@ -66,6 +71,9 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||||
|
github.com/nats-io/nats.go v1.39.0 // indirect
|
||||||
|
github.com/nats-io/nkeys v0.4.9 // indirect
|
||||||
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
github.com/oapi-codegen/runtime v1.1.1 // indirect
|
github.com/oapi-codegen/runtime v1.1.1 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
|
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
@@ -77,12 +85,12 @@ require (
|
|||||||
github.com/urfave/cli/v2 v2.27.5 // indirect
|
github.com/urfave/cli/v2 v2.27.5 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
golang.org/x/mod v0.22.0 // indirect
|
golang.org/x/mod v0.23.0 // indirect
|
||||||
golang.org/x/net v0.34.0 // indirect
|
golang.org/x/net v0.35.0 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
golang.org/x/tools v0.29.0 // indirect
|
golang.org/x/tools v0.30.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.1 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
77
go.sum
77
go.sum
@@ -6,6 +6,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
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 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
|
github.com/ClusterCockpit/cc-lib v0.0.0-20250216162506-b737e48423c5 h1:Suu9ivW5rz1dXAqHvA4V9dHI0jm4aIGzfpQGFvGzGEc=
|
||||||
|
github.com/ClusterCockpit/cc-lib v0.0.0-20250216162506-b737e48423c5/go.mod h1:T2iDLmsbGjyMy1UtDkR+hofoQXfTMuwSdeFettTav9Q=
|
||||||
github.com/ClusterCockpit/cc-units v0.4.0 h1:zP5DOu99GmErW0tCDf0gcLrlWt42RQ9dpoONEOh4cI0=
|
github.com/ClusterCockpit/cc-units v0.4.0 h1:zP5DOu99GmErW0tCDf0gcLrlWt42RQ9dpoONEOh4cI0=
|
||||||
github.com/ClusterCockpit/cc-units v0.4.0/go.mod h1:3S3PAhAayS3pbgcT4q9Vn9VJw22Op51X0YimtG77zBw=
|
github.com/ClusterCockpit/cc-units v0.4.0/go.mod h1:3S3PAhAayS3pbgcT4q9Vn9VJw22Op51X0YimtG77zBw=
|
||||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
@@ -38,6 +40,7 @@ github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/
|
|||||||
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
github.com/coreos/go-oidc/v3 v3.11.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 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -53,8 +56,14 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
|
|||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
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/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
|
||||||
|
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/frankban/quicktest v1.11.0/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
|
||||||
|
github.com/frankban/quicktest v1.11.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
|
||||||
|
github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk=
|
||||||
|
github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
|
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
@@ -83,6 +92,8 @@ github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17w
|
|||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
|
||||||
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
@@ -115,10 +126,17 @@ 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/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 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.13.0 h1:ioBbLmR5NMbAjP4UVA5r9b5xGjpABD7j65pI8kFphDM=
|
github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjwJdUHnwvfjMF71M1iI4=
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.13.0/go.mod h1:k+spCbt9hcvqvUiz0sr5D8LolXHqAAOfPw9v/RIRHl4=
|
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 h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
|
||||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
||||||
|
github.com/influxdata/line-protocol-corpus v0.0.0-20210519164801-ca6fa5da0184/go.mod h1:03nmhxzZ7Xk2pdG+lmMd7mHDfeVOYFyhOgwO61qWU98=
|
||||||
|
github.com/influxdata/line-protocol-corpus v0.0.0-20210922080147-aa28ccfb8937 h1:MHJNQ+p99hFATQm6ORoLmpUCF7ovjwEFshs/NHzAbig=
|
||||||
|
github.com/influxdata/line-protocol-corpus v0.0.0-20210922080147-aa28ccfb8937/go.mod h1:BKR9c0uHSmRgM/se9JhFHtTT7JTO67X23MtKMHtZcpo=
|
||||||
|
github.com/influxdata/line-protocol/v2 v2.0.0-20210312151457-c52fdecb625a/go.mod h1:6+9Xt5Sq1rWx+glMgxhcg2c0DUaehK+5TDcPZ76GypY=
|
||||||
|
github.com/influxdata/line-protocol/v2 v2.1.0/go.mod h1:QKw43hdUBg3GTk2iC3iyCxksNj7PX9aUSeYOYE/ceHY=
|
||||||
|
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 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
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=
|
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||||
@@ -142,10 +160,17 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX
|
|||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
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/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||||
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||||
@@ -171,6 +196,13 @@ 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/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 h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/nats-io/nats.go v1.39.0 h1:2/yg2JQjiYYKLwDuBzV0FbB2sIV+eFNkEevlRi4n9lI=
|
||||||
|
github.com/nats-io/nats.go v1.39.0/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM=
|
||||||
|
github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=
|
||||||
|
github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=
|
||||||
|
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
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/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 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
@@ -182,8 +214,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
@@ -238,14 +270,14 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg=
|
||||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
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-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
@@ -255,15 +287,15 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -273,8 +305,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
@@ -287,24 +319,29 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
|
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||||
|
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||||
|
@@ -208,7 +208,7 @@ const docTemplate = `{
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Success message",
|
"description": "Success message",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.DeleteJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -278,7 +278,7 @@ const docTemplate = `{
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Success message",
|
"description": "Success message",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.DeleteJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -348,7 +348,7 @@ const docTemplate = `{
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Success message",
|
"description": "Success message",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.DeleteJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -493,7 +493,7 @@ const docTemplate = `{
|
|||||||
"201": {
|
"201": {
|
||||||
"description": "Job added successfully",
|
"description": "Job added successfully",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.DefaultJobApiResponse"
|
"$ref": "#/definitions/api.StartJobApiResponse"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -587,7 +587,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"422": {
|
"422": {
|
||||||
"description": "Unprocessable Entity: job has already been stopped",
|
"description": "Unprocessable Entity: finding job failed: sql: no rows in result set",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/api.ErrorResponse"
|
"$ref": "#/definitions/api.ErrorResponse"
|
||||||
}
|
}
|
||||||
@@ -833,72 +833,6 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/notice/": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Modifies the content of notice.txt, shown as notice box on the homepage.\nIf more than one formValue is set then only the highest priority field is used.\nOnly accessible from IPs registered with apiAllowedIPs configuration option.",
|
|
||||||
"consumes": [
|
|
||||||
"multipart/form-data"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"text/plain"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"User"
|
|
||||||
],
|
|
||||||
"summary": "Updates or empties the notice box content",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Priority 1: New content to display",
|
|
||||||
"name": "new-content",
|
|
||||||
"in": "formData"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Success Response Message",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"422": {
|
|
||||||
"description": "Unprocessable Entity: The user could not be updated",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/user/{id}": {
|
"/user/{id}": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -1279,14 +1213,6 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api.DefaultJobApiResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"msg": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"api.DeleteJobApiRequest": {
|
"api.DeleteJobApiRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@@ -1310,6 +1236,14 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.DeleteJobApiResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.EditMetaRequest": {
|
"api.EditMetaRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -1396,6 +1330,14 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.StartJobApiResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"msg": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.StopJobApiRequest": {
|
"api.StopJobApiRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
57
internal/api/nats.go
Normal file
57
internal/api/nats.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
|
||||||
|
// All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ClusterCockpit/cc-backend/internal/config"
|
||||||
|
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
||||||
|
"github.com/ClusterCockpit/cc-backend/pkg/schema"
|
||||||
|
lp "github.com/ClusterCockpit/cc-lib/ccMessage"
|
||||||
|
"github.com/ClusterCockpit/cc-lib/sinks"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NatsClient struct {
|
||||||
|
SinkManager sinks.SinkManager
|
||||||
|
SinkChannel chan lp.CCMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
initOnce sync.Once
|
||||||
|
ni *NatsClient
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init(wg *sync.WaitGroup) {
|
||||||
|
initOnce.Do(func() {
|
||||||
|
ni = &NatsClient{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(config.Keys.SinkConfigFile) == 0 {
|
||||||
|
log.Error("Sink configuration file must be set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ni.SinkManager, err = sinks.New(wg, config.Keys.SinkConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ni.SinkChannel = make(chan lp.CCMessage, 200)
|
||||||
|
ni.SinkManager.AddInput(ni.SinkChannel)
|
||||||
|
ni.SinkManager.Start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Shutdown() {
|
||||||
|
if ni.SinkManager != nil {
|
||||||
|
log.Debug("Shutdown SinkManager...")
|
||||||
|
ni.SinkManager.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func forwardJob(job schema.BaseJob) {
|
||||||
|
payload := lp.NewEvent("start_job", nil , meta map[string]string, event string, tm time.Time)
|
||||||
|
}
|
@@ -757,7 +757,7 @@ func (api *RestApi) tagJob(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @accept json
|
// @accept json
|
||||||
// @produce json
|
// @produce json
|
||||||
// @param request body schema.JobMeta true "Job to add"
|
// @param request body schema.JobMeta true "Job to add"
|
||||||
// @success 201 {object} api.DefaultJobApiResponse "Job added successfully"
|
// @success 201 {object} api.StartJobApiResponse "Job added successfully"
|
||||||
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
||||||
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
||||||
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
||||||
@@ -772,8 +772,9 @@ func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.State == "" {
|
||||||
req.State = schema.JobStateRunning
|
req.State = schema.JobStateRunning
|
||||||
|
}
|
||||||
if err := importer.SanityChecks(&req.BaseJob); err != nil {
|
if err := importer.SanityChecks(&req.BaseJob); err != nil {
|
||||||
handleError(err, http.StatusBadRequest, rw)
|
handleError(err, http.StatusBadRequest, rw)
|
||||||
return
|
return
|
||||||
@@ -834,7 +835,7 @@ func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
||||||
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
||||||
// @failure 404 {object} api.ErrorResponse "Resource not found"
|
// @failure 404 {object} api.ErrorResponse "Resource not found"
|
||||||
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: job has already been stopped"
|
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: finding job failed: sql: no rows in result set"
|
||||||
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
|
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
|
||||||
// @security ApiKeyAuth
|
// @security ApiKeyAuth
|
||||||
// @router /jobs/stop_job/ [post]
|
// @router /jobs/stop_job/ [post]
|
||||||
@@ -870,7 +871,7 @@ func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @description Job to remove is specified by database ID. This will not remove the job from the job archive.
|
// @description Job to remove is specified by database ID. This will not remove the job from the job archive.
|
||||||
// @produce json
|
// @produce json
|
||||||
// @param id path int true "Database ID of Job"
|
// @param id path int true "Database ID of Job"
|
||||||
// @success 200 {object} api.DefaultJobApiResponse "Success message"
|
// @success 200 {object} api.DeleteJobApiResponse "Success message"
|
||||||
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
||||||
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
||||||
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
||||||
@@ -913,7 +914,7 @@ func (api *RestApi) deleteJobById(rw http.ResponseWriter, r *http.Request) {
|
|||||||
// @accept json
|
// @accept json
|
||||||
// @produce json
|
// @produce json
|
||||||
// @param request body api.DeleteJobApiRequest true "All fields required"
|
// @param request body api.DeleteJobApiRequest true "All fields required"
|
||||||
// @success 200 {object} api.DefaultJobApiResponse "Success message"
|
// @success 200 {object} api.DeleteJobApiResponse "Success message"
|
||||||
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
||||||
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
||||||
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
||||||
@@ -963,7 +964,7 @@ func (api *RestApi) deleteJobByRequest(rw http.ResponseWriter, r *http.Request)
|
|||||||
// @description Remove all jobs with start time before timestamp. The jobs will not be removed from the job archive.
|
// @description Remove all jobs with start time before timestamp. The jobs will not be removed from the job archive.
|
||||||
// @produce json
|
// @produce json
|
||||||
// @param ts path int true "Unix epoch timestamp"
|
// @param ts path int true "Unix epoch timestamp"
|
||||||
// @success 200 {object} api.DefaultJobApiResponse "Success message"
|
// @success 200 {object} api.DeleteJobApiResponse "Success message"
|
||||||
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
// @failure 400 {object} api.ErrorResponse "Bad Request"
|
||||||
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
// @failure 401 {object} api.ErrorResponse "Unauthorized"
|
||||||
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
// @failure 403 {object} api.ErrorResponse "Forbidden"
|
||||||
@@ -1003,13 +1004,8 @@ func (api *RestApi) deleteJobBefore(rw http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (api *RestApi) checkAndHandleStopJob(rw http.ResponseWriter, job *schema.Job, req StopJobApiRequest) {
|
func (api *RestApi) checkAndHandleStopJob(rw http.ResponseWriter, job *schema.Job, req StopJobApiRequest) {
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
if job.State != schema.JobStateRunning {
|
if job == nil || job.StartTime.Unix() >= req.StopTime || job.State != schema.JobStateRunning {
|
||||||
handleError(fmt.Errorf("jobId %d (id %d) on %s : job has already been stopped (state is: %s)", job.JobID, job.ID, job.Cluster, job.State), http.StatusUnprocessableEntity, rw)
|
handleError(fmt.Errorf("jobId %d (id %d) on %s : stopTime %d must be larger than startTime %d and only running jobs can be stopped (state is: %s)", job.JobID, job.ID, job.Cluster, req.StopTime, job.StartTime.Unix(), job.State), http.StatusBadRequest, rw)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if job == nil || job.StartTime.Unix() > req.StopTime {
|
|
||||||
handleError(fmt.Errorf("jobId %d (id %d) on %s : stopTime %d must be larger/equal than startTime %d", job.JobID, job.ID, job.Cluster, req.StopTime, job.StartTime.Unix()), http.StatusBadRequest, rw)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -60,13 +60,12 @@ func ArchiveJob(job *schema.Job, ctx context.Context) (*schema.JobMeta, error) {
|
|||||||
max = math.Max(max, series.Statistics.Max)
|
max = math.Max(max, series.Statistics.Max)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round AVG Result to 2 Digits
|
|
||||||
jobMeta.Statistics[metric] = schema.JobStatistics{
|
jobMeta.Statistics[metric] = schema.JobStatistics{
|
||||||
Unit: schema.Unit{
|
Unit: schema.Unit{
|
||||||
Prefix: archive.GetMetricConfig(job.Cluster, metric).Unit.Prefix,
|
Prefix: archive.GetMetricConfig(job.Cluster, metric).Unit.Prefix,
|
||||||
Base: archive.GetMetricConfig(job.Cluster, metric).Unit.Base,
|
Base: archive.GetMetricConfig(job.Cluster, metric).Unit.Base,
|
||||||
},
|
},
|
||||||
Avg: (math.Round((avg/float64(job.NumNodes))*100) / 100),
|
Avg: avg / float64(job.NumNodes),
|
||||||
Min: min,
|
Min: min,
|
||||||
Max: max,
|
Max: max,
|
||||||
}
|
}
|
||||||
|
@@ -10,11 +10,14 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/config"
|
"github.com/ClusterCockpit/cc-backend/internal/config"
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/repository"
|
"github.com/ClusterCockpit/cc-backend/internal/repository"
|
||||||
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
"github.com/ClusterCockpit/cc-backend/pkg/log"
|
||||||
@@ -32,6 +35,19 @@ var (
|
|||||||
authInstance *Authentication
|
authInstance *Authentication
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ipUserLimiters sync.Map
|
||||||
|
|
||||||
|
func getIPUserLimiter(ip, username string) *rate.Limiter {
|
||||||
|
key := ip + ":" + username
|
||||||
|
limiter, ok := ipUserLimiters.Load(key)
|
||||||
|
if !ok {
|
||||||
|
newLimiter := rate.NewLimiter(rate.Every(time.Hour/10), 10)
|
||||||
|
ipUserLimiters.Store(key, newLimiter)
|
||||||
|
return newLimiter
|
||||||
|
}
|
||||||
|
return limiter.(*rate.Limiter)
|
||||||
|
}
|
||||||
|
|
||||||
type Authentication struct {
|
type Authentication struct {
|
||||||
sessionStore *sessions.CookieStore
|
sessionStore *sessions.CookieStore
|
||||||
LdapAuth *LdapAuthenticator
|
LdapAuth *LdapAuthenticator
|
||||||
@@ -88,7 +104,7 @@ func Init() {
|
|||||||
authInstance.sessionStore = sessions.NewCookieStore(bytes)
|
authInstance.sessionStore = sessions.NewCookieStore(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d, err := time.ParseDuration(config.Keys.SessionMaxAge); err != nil {
|
if d, err := time.ParseDuration(config.Keys.SessionMaxAge); err == nil {
|
||||||
authInstance.SessionMaxAge = d
|
authInstance.SessionMaxAge = d
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,9 +224,21 @@ func (auth *Authentication) Login(
|
|||||||
onfailure func(rw http.ResponseWriter, r *http.Request, loginErr error),
|
onfailure func(rw http.ResponseWriter, r *http.Request, loginErr error),
|
||||||
) http.Handler {
|
) http.Handler {
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
username := r.FormValue("username")
|
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
var dbUser *schema.User
|
if err != nil {
|
||||||
|
ip = r.RemoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
username := r.FormValue("username")
|
||||||
|
|
||||||
|
limiter := getIPUserLimiter(ip, username)
|
||||||
|
if !limiter.Allow() {
|
||||||
|
log.Warnf("AUTH/RATE > Too many login attempts for combination IP: %s, Username: %s", ip, username)
|
||||||
|
onfailure(rw, r, errors.New("Too many login attempts, try again in a few minutes."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbUser *schema.User
|
||||||
if username != "" {
|
if username != "" {
|
||||||
var err error
|
var err error
|
||||||
dbUser, err = repository.GetUserRepository().GetUser(username)
|
dbUser, err = repository.GetUserRepository().GetUser(username)
|
||||||
|
44
internal/config/default_metrics.go
Normal file
44
internal/config/default_metrics.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DefaultMetricsCluster struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
DefaultMetrics string `json:"default_metrics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultMetricsConfig struct {
|
||||||
|
Clusters []DefaultMetricsCluster `json:"clusters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadDefaultMetricsConfig() (*DefaultMetricsConfig, error) {
|
||||||
|
filePath := "configs/default_metrics.json"
|
||||||
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var cfg DefaultMetricsConfig
|
||||||
|
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseMetricsString(s string) []string {
|
||||||
|
parts := strings.Split(s, ",")
|
||||||
|
var metrics []string
|
||||||
|
for _, p := range parts {
|
||||||
|
trimmed := strings.TrimSpace(p)
|
||||||
|
if trimmed != "" {
|
||||||
|
metrics = append(metrics, trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metrics
|
||||||
|
}
|
@@ -303,7 +303,6 @@ func (r *queryResolver) JobMetrics(ctx context.Context, id string, metrics []str
|
|||||||
|
|
||||||
// JobsFootprints is the resolver for the jobsFootprints field.
|
// JobsFootprints is the resolver for the jobsFootprints field.
|
||||||
func (r *queryResolver) JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error) {
|
func (r *queryResolver) JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error) {
|
||||||
// NOTE: Legacy Naming! This resolver is for normalized histograms in analysis view only - *Not* related to DB "footprint" column!
|
|
||||||
return r.jobsFootprints(ctx, filter, metrics)
|
return r.jobsFootprints(ctx, filter, metrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -170,9 +170,6 @@ func LoadData(job *schema.Job,
|
|||||||
jd.AddNodeScope("mem_bw")
|
jd.AddNodeScope("mem_bw")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round Resulting Stat Values
|
|
||||||
jd.RoundMetricStats()
|
|
||||||
|
|
||||||
return jd, ttl, size
|
return jd, ttl, size
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -440,23 +440,6 @@ func (ccms *CCMetricStore) buildQueries(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Core -> Socket
|
|
||||||
if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeSocket {
|
|
||||||
sockets, _ := topology.GetSocketsFromCores(hwthreads)
|
|
||||||
for _, socket := range sockets {
|
|
||||||
queries = append(queries, ApiQuery{
|
|
||||||
Metric: remoteName,
|
|
||||||
Hostname: host.Hostname,
|
|
||||||
Aggregate: true,
|
|
||||||
Type: &coreString,
|
|
||||||
TypeIds: intToStringSlice(topology.Socket[socket]),
|
|
||||||
Resolution: resolution,
|
|
||||||
})
|
|
||||||
assignedScope = append(assignedScope, scope)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Core -> Node
|
// Core -> Node
|
||||||
if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode {
|
if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode {
|
||||||
cores, _ := topology.GetCoresFromHWThreads(hwthreads)
|
cores, _ := topology.GetCoresFromHWThreads(hwthreads)
|
||||||
@@ -644,7 +627,7 @@ func (ccms *CCMetricStore) LoadNodeData(
|
|||||||
req.Queries = append(req.Queries, ApiQuery{
|
req.Queries = append(req.Queries, ApiQuery{
|
||||||
Hostname: node,
|
Hostname: node,
|
||||||
Metric: ccms.toRemoteName(metric),
|
Metric: ccms.toRemoteName(metric),
|
||||||
Resolution: 0, // Default for Node Queries: Will return metric $Timestep Resolution
|
Resolution: 60, // Default for Node Queries
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1055,23 +1038,6 @@ func (ccms *CCMetricStore) buildNodeQueries(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Core -> Socket
|
|
||||||
if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeSocket {
|
|
||||||
sockets, _ := topology.GetSocketsFromCores(topology.Node)
|
|
||||||
for _, socket := range sockets {
|
|
||||||
queries = append(queries, ApiQuery{
|
|
||||||
Metric: remoteName,
|
|
||||||
Hostname: hostname,
|
|
||||||
Aggregate: true,
|
|
||||||
Type: &coreString,
|
|
||||||
TypeIds: intToStringSlice(topology.Socket[socket]),
|
|
||||||
Resolution: resolution,
|
|
||||||
})
|
|
||||||
assignedScope = append(assignedScope, scope)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Core -> Node
|
// Core -> Node
|
||||||
if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode {
|
if nativeScope == schema.MetricScopeCore && scope == schema.MetricScopeNode {
|
||||||
cores, _ := topology.GetCoresFromHWThreads(topology.Node)
|
cores, _ := topology.GetCoresFromHWThreads(topology.Node)
|
||||||
|
@@ -217,6 +217,11 @@ func (r *JobRepository) UpdateMetadata(job *schema.Job, key, val string) (err er
|
|||||||
|
|
||||||
func (r *JobRepository) FetchFootprint(job *schema.Job) (map[string]float64, error) {
|
func (r *JobRepository) FetchFootprint(job *schema.Job) (map[string]float64, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
cachekey := fmt.Sprintf("footprint:%d", job.ID)
|
||||||
|
if cached := r.cache.Get(cachekey, nil); cached != nil {
|
||||||
|
job.Footprint = cached.(map[string]float64)
|
||||||
|
return job.Footprint, nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := sq.Select("job.footprint").From("job").Where("job.id = ?", job.ID).
|
if err := sq.Select("job.footprint").From("job").Where("job.id = ?", job.ID).
|
||||||
RunWith(r.stmtCache).QueryRow().Scan(&job.RawFootprint); err != nil {
|
RunWith(r.stmtCache).QueryRow().Scan(&job.RawFootprint); err != nil {
|
||||||
@@ -233,6 +238,7 @@ func (r *JobRepository) FetchFootprint(job *schema.Job) (map[string]float64, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.cache.Put(cachekey, job.Footprint, len(job.Footprint), 24*time.Hour)
|
||||||
log.Debugf("Timer FetchFootprint %s", time.Since(start))
|
log.Debugf("Timer FetchFootprint %s", time.Since(start))
|
||||||
return job.Footprint, nil
|
return job.Footprint, nil
|
||||||
}
|
}
|
||||||
@@ -600,11 +606,8 @@ func (r *JobRepository) UpdateEnergy(
|
|||||||
// FIXME: Needs sum as stats type
|
// FIXME: Needs sum as stats type
|
||||||
} else if sc.MetricConfig[i].Energy == "power" { // this metric has power as unit (Watt)
|
} else if sc.MetricConfig[i].Energy == "power" { // this metric has power as unit (Watt)
|
||||||
// Energy: Power (in Watts) * Time (in Seconds)
|
// Energy: Power (in Watts) * Time (in Seconds)
|
||||||
// Unit: (( W * s ) / 3600) / 1000 = kWh ; Rounded to 2 nearest digits: (Energy * 100) / 100
|
// Unit: ( W * s ) / 3600 / 1000 = kWh ; Rounded to 2 nearest digits
|
||||||
// Here: All-Node Metric Average * Number of Nodes * Job Runtime
|
energy = math.Round(((LoadJobStat(jobMeta, fp, "avg")*float64(jobMeta.Duration))/3600/1000)*100) / 100
|
||||||
// Note: Shared Jobs handled correctly since "Node Average" is based on partial resources, while "numNodes" factor is 1
|
|
||||||
metricNodeSum := LoadJobStat(jobMeta, fp, "avg") * float64(jobMeta.NumNodes) * float64(jobMeta.Duration)
|
|
||||||
energy = math.Round(((metricNodeSum/3600)/1000)*100) / 100
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("Error while collecting energy metric %s for job, DB ID '%v', return '0.0'", fp, jobMeta.ID)
|
log.Warnf("Error while collecting energy metric %s for job, DB ID '%v', return '0.0'", fp, jobMeta.ID)
|
||||||
|
@@ -176,9 +176,6 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select
|
|||||||
now := time.Now().Unix() // There does not seam to be a portable way to get the current unix timestamp accross different DBs.
|
now := time.Now().Unix() // There does not seam to be a portable way to get the current unix timestamp accross different DBs.
|
||||||
query = query.Where("(job.job_state != 'running' OR (? - job.start_time) > ?)", now, *filter.MinRunningFor)
|
query = query.Where("(job.job_state != 'running' OR (? - job.start_time) > ?)", now, *filter.MinRunningFor)
|
||||||
}
|
}
|
||||||
if filter.Exclusive != nil {
|
|
||||||
query = query.Where("job.exclusive = ?", *filter.Exclusive)
|
|
||||||
}
|
|
||||||
if filter.State != nil {
|
if filter.State != nil {
|
||||||
states := make([]string, len(filter.State))
|
states := make([]string, len(filter.State))
|
||||||
for i, val := range filter.State {
|
for i, val := range filter.State {
|
||||||
|
@@ -19,6 +19,7 @@ import (
|
|||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"github.com/ClusterCockpit/cc-backend/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -127,6 +128,30 @@ func (r *UserRepository) AddUser(user *schema.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("new user %#v created (roles: %s, auth-source: %d, projects: %s)", user.Username, rolesJson, user.AuthSource, projectsJson)
|
log.Infof("new user %#v created (roles: %s, auth-source: %d, projects: %s)", user.Username, rolesJson, user.AuthSource, projectsJson)
|
||||||
|
|
||||||
|
defaultMetricsCfg, err := config.LoadDefaultMetricsConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error loading default metrics config: %v", err)
|
||||||
|
} else if defaultMetricsCfg != nil {
|
||||||
|
for _, cluster := range defaultMetricsCfg.Clusters {
|
||||||
|
metricsArray := config.ParseMetricsString(cluster.DefaultMetrics)
|
||||||
|
metricsJSON, err := json.Marshal(metricsArray)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error marshaling default metrics for cluster %s: %v", cluster.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
confKey := "job_view_selectedMetrics:" + cluster.Name
|
||||||
|
if _, err := sq.Insert("configuration").
|
||||||
|
Columns("username", "confkey", "value").
|
||||||
|
Values(user.Username, confKey, string(metricsJSON)).
|
||||||
|
RunWith(r.DB).Exec(); err != nil {
|
||||||
|
log.Errorf("Error inserting default job view metrics for user %s and cluster %s: %v", user.Username, cluster.Name, err)
|
||||||
|
} else {
|
||||||
|
log.Infof("Default job view metrics for user %s and cluster %s set to %s", user.Username, cluster.Name, string(metricsJSON))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -94,7 +94,7 @@ func RegisterFootprintWorker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add values rounded to 2 digits: repo.LoadStats may return unrounded
|
// Add values rounded to 2 digits
|
||||||
jobMeta.Statistics[metric] = schema.JobStatistics{
|
jobMeta.Statistics[metric] = schema.JobStatistics{
|
||||||
Unit: schema.Unit{
|
Unit: schema.Unit{
|
||||||
Prefix: archive.GetMetricConfig(job.Cluster, metric).Unit.Prefix,
|
Prefix: archive.GetMetricConfig(job.Cluster, metric).Unit.Prefix,
|
||||||
|
@@ -122,38 +122,6 @@ func (topo *Topology) GetSocketsFromHWThreads(
|
|||||||
return sockets, exclusive
|
return sockets, exclusive
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a list of socket IDs given a list of core IDs. Even if just one
|
|
||||||
// core is in that socket, add it to the list. If no cores other than
|
|
||||||
// those in the argument list are assigned to one of the sockets in the first
|
|
||||||
// return value, return true as the second value. TODO: Optimize this, there
|
|
||||||
// must be a more efficient way/algorithm.
|
|
||||||
func (topo *Topology) GetSocketsFromCores (
|
|
||||||
cores []int,
|
|
||||||
) (sockets []int, exclusive bool) {
|
|
||||||
socketsMap := map[int]int{}
|
|
||||||
for _, core := range cores {
|
|
||||||
for _, hwthreadInCore := range topo.Core[core] {
|
|
||||||
for socket, hwthreadsInSocket := range topo.Socket {
|
|
||||||
for _, hwthreadInSocket := range hwthreadsInSocket {
|
|
||||||
if hwthreadInCore == hwthreadInSocket {
|
|
||||||
socketsMap[socket] += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exclusive = true
|
|
||||||
hwthreadsPerSocket := len(topo.Node) / len(topo.Socket)
|
|
||||||
sockets = make([]int, 0, len(socketsMap))
|
|
||||||
for socket, count := range socketsMap {
|
|
||||||
sockets = append(sockets, socket)
|
|
||||||
exclusive = exclusive && count == hwthreadsPerSocket
|
|
||||||
}
|
|
||||||
|
|
||||||
return sockets, exclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a list of core IDs given a list of hwthread IDs. Even if just one
|
// Return a list of core IDs given a list of hwthread IDs. Even if just one
|
||||||
// hwthread is in that core, add it to the list. If no hwthreads other than
|
// hwthread is in that core, add it to the list. If no hwthreads other than
|
||||||
// those in the argument list are assigned to one of the cores in the first
|
// those in the argument list are assigned to one of the cores in the first
|
||||||
|
@@ -148,6 +148,9 @@ type ProgramConfig struct {
|
|||||||
// redirect every request incoming at port 80 to that url.
|
// redirect every request incoming at port 80 to that url.
|
||||||
RedirectHttpTo string `json:"redirect-http-to"`
|
RedirectHttpTo string `json:"redirect-http-to"`
|
||||||
|
|
||||||
|
SinkConfigFile string `json:"sink-config-file"`
|
||||||
|
RecvConfigFile string `json:"recv-config-file"`
|
||||||
|
|
||||||
// If overwritten, at least all the options in the defaults below must
|
// If overwritten, at least all the options in the defaults below must
|
||||||
// be provided! Most options here can be overwritten by the user.
|
// be provided! Most options here can be overwritten by the user.
|
||||||
UiDefaults map[string]interface{} `json:"ui-defaults"`
|
UiDefaults map[string]interface{} `json:"ui-defaults"`
|
||||||
|
@@ -291,21 +291,6 @@ func (jd *JobData) AddNodeScope(metric string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jd *JobData) RoundMetricStats() {
|
|
||||||
// TODO: Make Digit-Precision Configurable? (Currently: Fixed to 2 Digits)
|
|
||||||
for _, scopes := range *jd {
|
|
||||||
for _, jm := range scopes {
|
|
||||||
for index := range jm.Series {
|
|
||||||
jm.Series[index].Statistics = MetricStatistics{
|
|
||||||
Avg: (math.Round(jm.Series[index].Statistics.Avg*100) / 100),
|
|
||||||
Min: (math.Round(jm.Series[index].Statistics.Min*100) / 100),
|
|
||||||
Max: (math.Round(jm.Series[index].Statistics.Max*100) / 100),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jm *JobMetric) AddPercentiles(ps []int) bool {
|
func (jm *JobMetric) AddPercentiles(ps []int) bool {
|
||||||
if jm.StatisticsSeries == nil {
|
if jm.StatisticsSeries == nil {
|
||||||
jm.AddStatisticsSeries()
|
jm.AddStatisticsSeries()
|
||||||
|
@@ -446,7 +446,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"job_view_selectedMetrics": {
|
"job_view_selectedMetrics": {
|
||||||
"description": "Initial metrics shown as plots in single job view",
|
"description": "",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@@ -117,41 +117,27 @@
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const roofQuery = gql`
|
|
||||||
query ($dbid: ID!, $selectedMetrics: [String!]!, $selectedScopes: [MetricScope!]!, $selectedResolution: Int) {
|
|
||||||
jobMetrics(id: $dbid, metrics: $selectedMetrics, scopes: $selectedScopes, resolution: $selectedResolution) {
|
|
||||||
name
|
|
||||||
scope
|
|
||||||
metric {
|
|
||||||
series {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
$: jobMetrics = queryStore({
|
$: jobMetrics = queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: query,
|
query: query,
|
||||||
variables: { dbid, selectedMetrics, selectedScopes },
|
variables: { dbid, selectedMetrics, selectedScopes },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Roofline: Always load roofMetrics with configured timestep (Resolution: 0)
|
|
||||||
$: roofMetrics = queryStore({
|
|
||||||
client: client,
|
|
||||||
query: roofQuery,
|
|
||||||
variables: { dbid, selectedMetrics: ["flops_any", "mem_bw"], selectedScopes: ["node"], selectedResolution: 0 },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle Job Query on Init -> is not executed anymore
|
// Handle Job Query on Init -> is not executed anymore
|
||||||
getContext("on-init")(() => {
|
getContext("on-init")(() => {
|
||||||
let job = $initq.data.job;
|
let job = $initq.data.job;
|
||||||
if (!job) return;
|
if (!job) return;
|
||||||
|
|
||||||
const pendingMetrics = [
|
const pendingMetrics = [
|
||||||
|
"flops_any",
|
||||||
|
"mem_bw",
|
||||||
...(ccconfig[`job_view_selectedMetrics:${job.cluster}`] ||
|
...(ccconfig[`job_view_selectedMetrics:${job.cluster}`] ||
|
||||||
ccconfig[`job_view_selectedMetrics`]
|
$initq.data.globalMetrics.reduce((names, gm) => {
|
||||||
|
if (gm.availability.find((av) => av.cluster === job.cluster)) {
|
||||||
|
names.push(gm.name);
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}, [])
|
||||||
),
|
),
|
||||||
...(ccconfig[`job_view_nodestats_selectedMetrics:${job.cluster}`] ||
|
...(ccconfig[`job_view_nodestats_selectedMetrics:${job.cluster}`] ||
|
||||||
ccconfig[`job_view_nodestats_selectedMetrics`]
|
ccconfig[`job_view_nodestats_selectedMetrics`]
|
||||||
@@ -290,12 +276,12 @@ const roofQuery = gql`
|
|||||||
|
|
||||||
<!-- Column 3: Job Roofline; If footprint Enabled: full width, else half width -->
|
<!-- Column 3: Job Roofline; If footprint Enabled: full width, else half width -->
|
||||||
<Col xs={12} md={12} xl={5} xxl={6}>
|
<Col xs={12} md={12} xl={5} xxl={6}>
|
||||||
{#if $initq.error || $roofMetrics.error}
|
{#if $initq.error || $jobMetrics.error}
|
||||||
<Card body color="danger">
|
<Card body color="danger">
|
||||||
<p>Initq Error: {$initq.error?.message}</p>
|
<p>Initq Error: {$initq.error?.message}</p>
|
||||||
<p>roofMetrics (jobMetrics) Error: {$roofMetrics.error?.message}</p>
|
<p>jobMetrics Error: {$jobMetrics.error?.message}</p>
|
||||||
</Card>
|
</Card>
|
||||||
{:else if $initq?.data && $roofMetrics?.data}
|
{:else if $initq?.data && $jobMetrics?.data}
|
||||||
<Card style="height: 400px;">
|
<Card style="height: 400px;">
|
||||||
<div bind:clientWidth={roofWidth}>
|
<div bind:clientWidth={roofWidth}>
|
||||||
<Roofline
|
<Roofline
|
||||||
@@ -306,10 +292,10 @@ const roofQuery = gql`
|
|||||||
.find((c) => c.name == $initq.data.job.cluster)
|
.find((c) => c.name == $initq.data.job.cluster)
|
||||||
.subClusters.find((sc) => sc.name == $initq.data.job.subCluster)}
|
.subClusters.find((sc) => sc.name == $initq.data.job.subCluster)}
|
||||||
data={transformDataForRoofline(
|
data={transformDataForRoofline(
|
||||||
$roofMetrics.data?.jobMetrics?.find(
|
$jobMetrics.data?.jobMetrics?.find(
|
||||||
(m) => m.name == "flops_any" && m.scope == "node",
|
(m) => m.name == "flops_any" && m.scope == "node",
|
||||||
)?.metric,
|
)?.metric,
|
||||||
$roofMetrics.data?.jobMetrics?.find(
|
$jobMetrics.data?.jobMetrics?.find(
|
||||||
(m) => m.name == "mem_bw" && m.scope == "node",
|
(m) => m.name == "mem_bw" && m.scope == "node",
|
||||||
)?.metric,
|
)?.metric,
|
||||||
)}
|
)}
|
||||||
|
@@ -80,7 +80,6 @@
|
|||||||
: ccconfig.user_view_histogramMetrics || [];
|
: ccconfig.user_view_histogramMetrics || [];
|
||||||
|
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
// Note: nodeMetrics are requested on configured $timestep resolution
|
|
||||||
$: mainQuery = queryStore({
|
$: mainQuery = queryStore({
|
||||||
client: client,
|
client: client,
|
||||||
query: gql`
|
query: gql`
|
||||||
|
@@ -77,7 +77,6 @@
|
|||||||
for (let sm of systemMetrics) {
|
for (let sm of systemMetrics) {
|
||||||
systemUnits[sm.name] = (sm?.unit?.prefix ? sm.unit.prefix : "") + (sm?.unit?.base ? sm.unit.base : "")
|
systemUnits[sm.name] = (sm?.unit?.prefix ? sm.unit.prefix : "") + (sm?.unit?.base ? sm.unit.base : "")
|
||||||
}
|
}
|
||||||
if (!selectedMetric) selectedMetric = systemMetrics[0].name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: loadMetrics($initialized)
|
$: loadMetrics($initialized)
|
||||||
|
@@ -2,12 +2,16 @@
|
|||||||
@component Polar Plot based on chartJS Radar
|
@component Polar Plot based on chartJS Radar
|
||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
- `polarMetrics [Object]?`: Metric names and scaled peak values for rendering polar plot [Default: [] ]
|
- `footprintData [Object]?`: job.footprint content, evaluated in regards to peak config in jobSummary.svelte [Default: null]
|
||||||
|
- `metrics [String]?`: Metric names to display as polar plot [Default: null]
|
||||||
|
- `cluster GraphQL.Cluster?`: Cluster Object of the parent job [Default: null]
|
||||||
|
- `subCluster GraphQL.SubCluster?`: SubCluster Object of the parent job [Default: null]
|
||||||
- `jobMetrics [GraphQL.JobMetricWithName]?`: Metric data [Default: null]
|
- `jobMetrics [GraphQL.JobMetricWithName]?`: Metric data [Default: null]
|
||||||
- `height Number?`: Plot height [Default: 365]
|
- `height Number?`: Plot height [Default: 365]
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { getContext } from 'svelte'
|
||||||
import { Radar } from 'svelte-chartjs';
|
import { Radar } from 'svelte-chartjs';
|
||||||
import {
|
import {
|
||||||
Chart as ChartJS,
|
Chart as ChartJS,
|
||||||
@@ -30,37 +34,54 @@
|
|||||||
LineElement
|
LineElement
|
||||||
);
|
);
|
||||||
|
|
||||||
export let polarMetrics = [];
|
export let footprintData = null;
|
||||||
|
export let metrics = null;
|
||||||
|
export let cluster = null;
|
||||||
|
export let subCluster = null;
|
||||||
export let jobMetrics = null;
|
export let jobMetrics = null;
|
||||||
export let height = 350;
|
export let height = 350;
|
||||||
|
|
||||||
const labels = polarMetrics
|
function getLabels() {
|
||||||
.filter((m) => (m.peak != null))
|
if (footprintData) {
|
||||||
.map(pm => pm.name)
|
return footprintData.filter(fpd => {
|
||||||
.sort(function (a, b) {return ((a > b) ? 1 : ((b > a) ? -1 : 0))});
|
if (!jobMetrics.find(m => m.name == fpd.name && m.scope == "node" || fpd.impact == 4)) {
|
||||||
|
console.warn(`PolarPlot: No metric data for '${fpd.name}'`)
|
||||||
function loadData(type) {
|
return false
|
||||||
if (!labels) {
|
}
|
||||||
console.warn("Empty 'polarMetrics' array prop! Cannot render Polar representation.")
|
return true
|
||||||
return []
|
})
|
||||||
|
.map(filtered => filtered.name)
|
||||||
|
.sort(function (a, b) {
|
||||||
|
return ((a > b) ? 1 : ((b > a) ? -1 : 0));
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
if (type === 'avg') {
|
return metrics.filter(name => {
|
||||||
return getValues(getAvg)
|
if (!jobMetrics.find(m => m.name == name && m.scope == "node")) {
|
||||||
} else if (type === 'max') {
|
console.warn(`PolarPlot: No metric data for '${name}'`)
|
||||||
return getValues(getMax)
|
return false
|
||||||
} else if (type === 'min') {
|
|
||||||
return getValues(getMin)
|
|
||||||
}
|
}
|
||||||
console.log('Unknown Type For Polar Data (must be one of [min, max, avg])')
|
return true
|
||||||
return []
|
})
|
||||||
|
.sort(function (a, b) {
|
||||||
|
return ((a > b) ? 1 : ((b > a) ? -1 : 0));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers
|
const labels = getLabels();
|
||||||
|
const getMetricConfig = getContext("getMetricConfig");
|
||||||
|
|
||||||
const getValues = (getStat) => labels.map(name => {
|
const getValuesForStatGeneric = (getStat) => labels.map(name => {
|
||||||
// Peak is adapted and scaled for job shared state
|
// TODO: Requires Scaling if Shared Job
|
||||||
const peak = polarMetrics.find(m => m.name == name).peak
|
const peak = getMetricConfig(cluster, subCluster, name).peak
|
||||||
|
const metric = jobMetrics.find(m => m.name == name && m.scope == "node")
|
||||||
|
const value = getStat(metric.metric) / peak
|
||||||
|
return value <= 1. ? value : 1.
|
||||||
|
})
|
||||||
|
|
||||||
|
const getValuesForStatFootprint = (getStat) => labels.map(name => {
|
||||||
|
// FootprintData 'Peak' is pre-scaled for Shared Jobs in JobSummary Component
|
||||||
|
const peak = footprintData.find(fpd => fpd.name === name).peak
|
||||||
const metric = jobMetrics.find(m => m.name == name && m.scope == "node")
|
const metric = jobMetrics.find(m => m.name == name && m.scope == "node")
|
||||||
const value = getStat(metric.metric) / peak
|
const value = getStat(metric.metric) / peak
|
||||||
return value <= 1. ? value : 1.
|
return value <= 1. ? value : 1.
|
||||||
@@ -87,14 +108,36 @@
|
|||||||
return avg / metric.series.length
|
return avg / metric.series.length
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chart JS Objects
|
function loadDataGeneric(type) {
|
||||||
|
if (type === 'avg') {
|
||||||
|
return getValuesForStatGeneric(getAvg)
|
||||||
|
} else if (type === 'max') {
|
||||||
|
return getValuesForStatGeneric(getMax)
|
||||||
|
} else if (type === 'min') {
|
||||||
|
return getValuesForStatGeneric(getMin)
|
||||||
|
}
|
||||||
|
console.log('Unknown Type For Polar Data')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDataForFootprint(type) {
|
||||||
|
if (type === 'avg') {
|
||||||
|
return getValuesForStatFootprint(getAvg)
|
||||||
|
} else if (type === 'max') {
|
||||||
|
return getValuesForStatFootprint(getMax)
|
||||||
|
} else if (type === 'min') {
|
||||||
|
return getValuesForStatFootprint(getMin)
|
||||||
|
}
|
||||||
|
console.log('Unknown Type For Polar Data')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Max',
|
label: 'Max',
|
||||||
data: loadData('max'), // Node Scope Only
|
data: footprintData ? loadDataForFootprint('max') : loadDataGeneric('max'), // Node Scope Only
|
||||||
fill: 1,
|
fill: 1,
|
||||||
backgroundColor: 'rgba(0, 0, 255, 0.25)',
|
backgroundColor: 'rgba(0, 0, 255, 0.25)',
|
||||||
borderColor: 'rgb(0, 0, 255)',
|
borderColor: 'rgb(0, 0, 255)',
|
||||||
@@ -105,7 +148,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Avg',
|
label: 'Avg',
|
||||||
data: loadData('avg'), // Node Scope Only
|
data: footprintData ? loadDataForFootprint('avg') : loadDataGeneric('avg'), // Node Scope Only
|
||||||
fill: 2,
|
fill: 2,
|
||||||
backgroundColor: 'rgba(255, 210, 0, 0.25)',
|
backgroundColor: 'rgba(255, 210, 0, 0.25)',
|
||||||
borderColor: 'rgb(255, 210, 0)',
|
borderColor: 'rgb(255, 210, 0)',
|
||||||
@@ -116,7 +159,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Min',
|
label: 'Min',
|
||||||
data: loadData('min'), // Node Scope Only
|
data: footprintData ? loadDataForFootprint('min') : loadDataGeneric('min'), // Node Scope Only
|
||||||
fill: true,
|
fill: true,
|
||||||
backgroundColor: 'rgba(255, 0, 0, 0.25)',
|
backgroundColor: 'rgba(255, 0, 0, 0.25)',
|
||||||
borderColor: 'rgb(255, 0, 0)',
|
borderColor: 'rgb(255, 0, 0)',
|
||||||
|
@@ -30,25 +30,9 @@
|
|||||||
export let height = "400px";
|
export let height = "400px";
|
||||||
|
|
||||||
const ccconfig = getContext("cc-config")
|
const ccconfig = getContext("cc-config")
|
||||||
const globalMetrics = getContext("globalMetrics")
|
const showFootprint = !!ccconfig[`job_view_showFootprint`];
|
||||||
const showFootprintTab = !!ccconfig[`job_view_showFootprint`];
|
|
||||||
|
|
||||||
// Metrics Configured To Be Footprints For (sub)Cluster
|
const footprintData = job?.footprint?.map((jf) => {
|
||||||
const clusterFootprintMetrics = getContext("clusters")
|
|
||||||
.find((c) => c.name == job.cluster)?.subClusters
|
|
||||||
.find((sc) => sc.name == job.subCluster)?.footprint || []
|
|
||||||
|
|
||||||
// Data For Polarplot Will Be Calculated Based On JobMetrics And Thresholds
|
|
||||||
const polarMetrics = globalMetrics.reduce((pms, gm) => {
|
|
||||||
if (clusterFootprintMetrics.includes(gm.name)) {
|
|
||||||
const fmt = findJobFootprintThresholds(job, gm.footprint, getContext("getMetricConfig")(job.cluster, job.subCluster, gm.name));
|
|
||||||
pms.push({ name: gm.name, peak: fmt ? fmt.peak : null });
|
|
||||||
}
|
|
||||||
return pms;
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Prepare Job Footprint Data Based On Values Saved In Database
|
|
||||||
const jobFootprintData = !showFootprintTab ? null : job?.footprint?.map((jf) => {
|
|
||||||
const fmc = getContext("getMetricConfig")(job.cluster, job.subCluster, jf.name);
|
const fmc = getContext("getMetricConfig")(job.cluster, job.subCluster, jf.name);
|
||||||
if (fmc) {
|
if (fmc) {
|
||||||
// Unit
|
// Unit
|
||||||
@@ -203,16 +187,16 @@
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
$: summaryMessages = writeSummary(jobFootprintData)
|
$: summaryMessages = writeSummary(footprintData)
|
||||||
*/
|
*/
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card class="overflow-auto" style="width: {width}; height: {height}">
|
<Card class="overflow-auto" style="width: {width}; height: {height}">
|
||||||
<TabContent> <!-- on:tab={(e) => (status = e.detail)} -->
|
<TabContent> <!-- on:tab={(e) => (status = e.detail)} -->
|
||||||
{#if showFootprintTab}
|
{#if showFootprint}
|
||||||
<TabPane tabId="foot" tab="Footprint" active>
|
<TabPane tabId="foot" tab="Footprint" active>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{#each jobFootprintData as fpd, index}
|
{#each footprintData as fpd, index}
|
||||||
{#if fpd.impact !== 4}
|
{#if fpd.impact !== 4}
|
||||||
<div class="mb-1 d-flex justify-content-between">
|
<div class="mb-1 d-flex justify-content-between">
|
||||||
<div> <b>{fpd.name} ({fpd.stat})</b></div>
|
<div> <b>{fpd.name} ({fpd.stat})</b></div>
|
||||||
@@ -253,7 +237,7 @@
|
|||||||
>{fpd.message}</Tooltip
|
>{fpd.message}</Tooltip
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<Row cols={12} class="{(jobFootprintData.length == (index + 1)) ? 'mb-0' : 'mb-2'}">
|
<Row cols={12} class="{(footprintData.length == (index + 1)) ? 'mb-0' : 'mb-2'}">
|
||||||
{#if fpd.dir}
|
{#if fpd.dir}
|
||||||
<Col xs="1">
|
<Col xs="1">
|
||||||
<Icon name="caret-left-fill" />
|
<Icon name="caret-left-fill" />
|
||||||
@@ -295,10 +279,10 @@
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
{/if}
|
{/if}
|
||||||
<TabPane tabId="polar" tab="Polar" active={!showFootprintTab}>
|
<TabPane tabId="polar" tab="Polar" active={!showFootprint}>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Polar
|
<Polar
|
||||||
{polarMetrics}
|
{footprintData}
|
||||||
{jobMetrics}
|
{jobMetrics}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
@@ -148,17 +148,16 @@
|
|||||||
zoomState = {...pendingZoomState}
|
zoomState = {...pendingZoomState}
|
||||||
}
|
}
|
||||||
|
|
||||||
// On additional scope request
|
// Set selected scope to min of returned scopes
|
||||||
if (selectedScope == "load-all") {
|
if (selectedScope == "load-all") {
|
||||||
// Push scope to statsTable (Needs to be in this case, else newly selected 'Metric.svelte' renders cause statsTable race condition)
|
selectedScope = minScope(scopes)
|
||||||
|
nodeOnly = (selectedScope == "node") // "node" still only scope after load-all
|
||||||
|
}
|
||||||
|
|
||||||
const statsTableData = $metricData.data.singleUpdate.filter((x) => x.scope !== "node")
|
const statsTableData = $metricData.data.singleUpdate.filter((x) => x.scope !== "node")
|
||||||
if (statsTableData.length > 0) {
|
if (statsTableData.length > 0) {
|
||||||
dispatch("more-loaded", statsTableData);
|
dispatch("more-loaded", statsTableData);
|
||||||
}
|
}
|
||||||
// Set selected scope to min of returned scopes
|
|
||||||
selectedScope = minScope(scopes)
|
|
||||||
nodeOnly = (selectedScope == "node") // "node" still only scope after load-all
|
|
||||||
}
|
|
||||||
|
|
||||||
patternMatches = statsPattern.exec(selectedScope)
|
patternMatches = statsPattern.exec(selectedScope)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user