Merge hotfix changes

This commit is contained in:
Jan Eitzinger 2025-04-24 11:07:02 +02:00
commit 8dfa1957f4
5 changed files with 71 additions and 61 deletions

View File

@ -15,9 +15,8 @@
"version": "1.0.0"
},
"host": "localhost:8080",
"basePath": "/api",
"paths": {
"/clusters/": {
"/api/clusters/": {
"get": {
"security": [
{
@ -74,7 +73,7 @@
}
}
},
"/jobs/": {
"/api/jobs/": {
"get": {
"security": [
{
@ -169,7 +168,7 @@
}
}
},
"/jobs/delete_job/": {
"/api/jobs/delete_job/": {
"delete": {
"security": [
{
@ -244,7 +243,7 @@
}
}
},
"/jobs/delete_job/{id}": {
"/api/jobs/delete_job/{id}": {
"delete": {
"security": [
{
@ -314,7 +313,7 @@
}
}
},
"/jobs/delete_job_before/{ts}": {
"/api/jobs/delete_job_before/{ts}": {
"delete": {
"security": [
{
@ -384,7 +383,7 @@
}
}
},
"/jobs/edit_meta/{id}": {
"/api/jobs/edit_meta/{id}": {
"post": {
"security": [
{
@ -454,7 +453,7 @@
}
}
},
"/jobs/start_job/": {
"/api/jobs/start_job/": {
"post": {
"security": [
{
@ -523,7 +522,7 @@
}
}
},
"/jobs/stop_job/": {
"/api/jobs/stop_job/": {
"post": {
"security": [
{
@ -595,7 +594,7 @@
}
}
},
"/jobs/tag_job/{id}": {
"/api/jobs/tag_job/{id}": {
"post": {
"security": [
{
@ -668,7 +667,7 @@
}
}
},
"/jobs/{id}": {
"/api/jobs/{id}": {
"get": {
"security": [
{
@ -827,7 +826,7 @@
}
}
},
"/notice/": {
"/config/notice/": {
"post": {
"security": [
{
@ -893,7 +892,7 @@
}
}
},
"/user/{id}": {
"/config/user/{id}": {
"post": {
"security": [
{
@ -998,7 +997,7 @@
}
}
},
"/users/": {
"/config/users/": {
"get": {
"security": [
{

View File

@ -1,4 +1,3 @@
basePath: /api
definitions:
api.ApiReturnedUser:
properties:
@ -671,7 +670,7 @@ info:
title: ClusterCockpit REST API
version: 1.0.0
paths:
/clusters/:
/api/clusters/:
get:
description: Get a list of all cluster configs. Specific cluster can be requested
using query parameter.
@ -708,7 +707,7 @@ paths:
summary: Lists all cluster configs
tags:
- Cluster query
/jobs/:
/api/jobs/:
get:
description: |-
Get a list of all jobs. Filters can be applied using query parameters.
@ -773,7 +772,7 @@ paths:
summary: Lists all jobs
tags:
- Job query
/jobs/{id}:
/api/jobs/{id}:
get:
description: |-
Job to get is specified by database ID
@ -882,7 +881,7 @@ paths:
summary: Get job meta and configurable metric data
tags:
- Job query
/jobs/delete_job/:
/api/jobs/delete_job/:
delete:
consumes:
- application/json
@ -932,7 +931,7 @@ paths:
summary: Remove a job from the sql database
tags:
- Job remove
/jobs/delete_job/{id}:
/api/jobs/delete_job/{id}:
delete:
description: Job to remove is specified by database ID. This will not remove
the job from the job archive.
@ -979,7 +978,7 @@ paths:
summary: Remove a job from the sql database
tags:
- Job remove
/jobs/delete_job_before/{ts}:
/api/jobs/delete_job_before/{ts}:
delete:
description: Remove all jobs with start time before timestamp. The jobs will
not be removed from the job archive.
@ -1026,7 +1025,7 @@ paths:
summary: Remove a job from the sql database
tags:
- Job remove
/jobs/edit_meta/{id}:
/api/jobs/edit_meta/{id}:
post:
consumes:
- application/json
@ -1073,7 +1072,7 @@ paths:
summary: Edit meta-data json
tags:
- Job add and modify
/jobs/start_job/:
/api/jobs/start_job/:
post:
consumes:
- application/json
@ -1120,7 +1119,7 @@ paths:
summary: Adds a new job as "running"
tags:
- Job add and modify
/jobs/stop_job/:
/api/jobs/stop_job/:
post:
description: |-
Job to stop is specified by request body. All fields are required in this case.
@ -1168,7 +1167,7 @@ paths:
summary: Marks job as completed and triggers archiving
tags:
- Job add and modify
/jobs/tag_job/{id}:
/api/jobs/tag_job/{id}:
post:
consumes:
- application/json
@ -1218,7 +1217,7 @@ paths:
summary: Adds one or more tags to a job
tags:
- Job add and modify
/notice/:
/config/notice/:
post:
consumes:
- multipart/form-data
@ -1263,7 +1262,7 @@ paths:
summary: Updates or empties the notice box content
tags:
- User
/user/{id}:
/config/user/{id}:
post:
consumes:
- multipart/form-data
@ -1337,7 +1336,7 @@ paths:
summary: Updates an existing user
tags:
- User
/users/:
/config/users/:
delete:
consumes:
- multipart/form-data

View File

@ -18,6 +18,7 @@ import (
"time"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/ClusterCockpit/cc-backend/internal/api"
"github.com/ClusterCockpit/cc-backend/internal/archiver"
@ -31,6 +32,7 @@ import (
"github.com/ClusterCockpit/cc-backend/web"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
httpSwagger "github.com/swaggo/http-swagger"
)
@ -53,13 +55,24 @@ func serverInit() {
// Setup the http.Handler/Router used by the server
graph.Init()
resolver := graph.GetResolverInstance()
graphQLEndpoint := handler.NewDefaultServer(
graphQLServer := handler.New(
generated.NewExecutableSchema(generated.Config{Resolvers: resolver}))
graphQLServer.AddTransport(transport.SSE{})
graphQLServer.AddTransport(transport.POST{})
graphQLServer.AddTransport(transport.Websocket{
KeepAlivePingInterval: 10 * time.Second,
Upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
},
})
if os.Getenv("DEBUG") != "1" {
// Having this handler means that a error message is returned via GraphQL instead of the connection simply beeing closed.
// The problem with this is that then, no more stacktrace is printed to stderr.
graphQLEndpoint.SetRecoverFunc(func(ctx context.Context, err interface{}) error {
graphQLServer.SetRecoverFunc(func(ctx context.Context, err any) error {
switch e := err.(type) {
case string:
return fmt.Errorf("MAIN > Panic: %s", e)
@ -78,7 +91,7 @@ func serverInit() {
router = mux.NewRouter()
buildInfo := web.Build{Version: version, Hash: commit, Buildtime: date}
info := map[string]interface{}{}
info := map[string]any{}
info["hasOpenIDConnect"] = false
if config.Keys.OpenIDConfig != nil {
@ -208,7 +221,7 @@ func serverInit() {
router.PathPrefix("/swagger/").Handler(httpSwagger.Handler(
httpSwagger.URL("http://" + config.Keys.Addr + "/swagger/doc.json"))).Methods(http.MethodGet)
}
secured.Handle("/query", graphQLEndpoint)
secured.Handle("/query", graphQLServer)
// Send a searchId and then reply with a redirect to a user, or directly send query to job table for jobid and project.
secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) {

View File

@ -23,7 +23,7 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/clusters/": {
"/api/clusters/": {
"get": {
"security": [
{
@ -80,7 +80,7 @@ const docTemplate = `{
}
}
},
"/jobs/": {
"/api/jobs/": {
"get": {
"security": [
{
@ -175,7 +175,7 @@ const docTemplate = `{
}
}
},
"/jobs/delete_job/": {
"/api/jobs/delete_job/": {
"delete": {
"security": [
{
@ -250,7 +250,7 @@ const docTemplate = `{
}
}
},
"/jobs/delete_job/{id}": {
"/api/jobs/delete_job/{id}": {
"delete": {
"security": [
{
@ -320,7 +320,7 @@ const docTemplate = `{
}
}
},
"/jobs/delete_job_before/{ts}": {
"/api/jobs/delete_job_before/{ts}": {
"delete": {
"security": [
{
@ -390,7 +390,7 @@ const docTemplate = `{
}
}
},
"/jobs/edit_meta/{id}": {
"/api/jobs/edit_meta/{id}": {
"post": {
"security": [
{
@ -460,7 +460,7 @@ const docTemplate = `{
}
}
},
"/jobs/start_job/": {
"/api/jobs/start_job/": {
"post": {
"security": [
{
@ -529,7 +529,7 @@ const docTemplate = `{
}
}
},
"/jobs/stop_job/": {
"/api/jobs/stop_job/": {
"post": {
"security": [
{
@ -601,7 +601,7 @@ const docTemplate = `{
}
}
},
"/jobs/tag_job/{id}": {
"/api/jobs/tag_job/{id}": {
"post": {
"security": [
{
@ -674,7 +674,7 @@ const docTemplate = `{
}
}
},
"/jobs/{id}": {
"/api/jobs/{id}": {
"get": {
"security": [
{
@ -833,7 +833,7 @@ const docTemplate = `{
}
}
},
"/notice/": {
"/config/notice/": {
"post": {
"security": [
{
@ -899,7 +899,7 @@ const docTemplate = `{
}
}
},
"/user/{id}": {
"/config/user/{id}": {
"post": {
"security": [
{
@ -1004,7 +1004,7 @@ const docTemplate = `{
}
}
},
"/users/": {
"/config/users/": {
"get": {
"security": [
{
@ -2191,7 +2191,7 @@ const docTemplate = `{
var SwaggerInfo = &swag.Spec{
Version: "1.0.0",
Host: "localhost:8080",
BasePath: "/api",
BasePath: "",
Schemes: []string{},
Title: "ClusterCockpit REST API",
Description: "API for batch job control.",

View File

@ -46,7 +46,6 @@ import (
// @license.url https://opensource.org/licenses/MIT
// @host localhost:8080
// @basePath /api
// @securityDefinitions.apikey ApiKeyAuth
// @in header
@ -220,7 +219,7 @@ func handleError(err error, statusCode int, rw http.ResponseWriter) {
})
}
func decode(r io.Reader, val interface{}) error {
func decode(r io.Reader, val any) error {
dec := json.NewDecoder(r)
dec.DisallowUnknownFields()
return dec.Decode(val)
@ -238,7 +237,7 @@ func decode(r io.Reader, val interface{}) error {
// @failure 403 {object} api.ErrorResponse "Forbidden"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /clusters/ [get]
// @router /api/clusters/ [get]
func (api *RestApi) getClusters(rw http.ResponseWriter, r *http.Request) {
if user := repository.GetUserFromContext(r.Context()); user != nil &&
!user.HasRole(schema.RoleApi) {
@ -293,7 +292,7 @@ func (api *RestApi) getClusters(rw http.ResponseWriter, r *http.Request) {
// @failure 403 {object} api.ErrorResponse "Forbidden"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/ [get]
// @router /api/jobs/ [get]
func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
withMetadata := false
filter := &model.JobFilter{}
@ -427,7 +426,7 @@ func (api *RestApi) getJobs(rw http.ResponseWriter, r *http.Request) {
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: finding job failed: sql: no rows in result set"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/{id} [get]
// @router /api/jobs/{id} [get]
func (api *RestApi) getCompleteJobById(rw http.ResponseWriter, r *http.Request) {
// Fetch job from db
id, ok := mux.Vars(r)["id"]
@ -520,7 +519,7 @@ func (api *RestApi) getCompleteJobById(rw http.ResponseWriter, r *http.Request)
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: finding job failed: sql: no rows in result set"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/{id} [post]
// @router /api/jobs/{id} [post]
func (api *RestApi) getJobById(rw http.ResponseWriter, r *http.Request) {
// Fetch job from db
id, ok := mux.Vars(r)["id"]
@ -624,7 +623,7 @@ func (api *RestApi) getJobById(rw http.ResponseWriter, r *http.Request) {
// @failure 404 {object} api.ErrorResponse "Job does not exist"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/edit_meta/{id} [post]
// @router /api/jobs/edit_meta/{id} [post]
func (api *RestApi) editMeta(rw http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
if err != nil {
@ -670,7 +669,7 @@ func (api *RestApi) editMeta(rw http.ResponseWriter, r *http.Request) {
// @failure 404 {object} api.ErrorResponse "Job or tag does not exist"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/tag_job/{id} [post]
// @router /api/jobs/tag_job/{id} [post]
func (api *RestApi) tagJob(rw http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
if err != nil {
@ -839,7 +838,7 @@ func (api *RestApi) removeTags(rw http.ResponseWriter, r *http.Request) {
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: The combination of jobId, clusterId and startTime does already exist"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/start_job/ [post]
// @router /api/jobs/start_job/ [post]
func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
req := schema.JobMeta{BaseJob: schema.JobDefaults}
if err := decode(r.Body, &req); err != nil {
@ -912,7 +911,7 @@ func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: job has already been stopped"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/stop_job/ [post]
// @router /api/jobs/stop_job/ [post]
func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) {
// Parse request body
req := StopJobApiRequest{}
@ -953,7 +952,7 @@ func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) {
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: finding job failed: sql: no rows in result set"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/delete_job/{id} [delete]
// @router /api/jobs/delete_job/{id} [delete]
func (api *RestApi) deleteJobById(rw http.ResponseWriter, r *http.Request) {
// Fetch job (that will be stopped) from db
id, ok := mux.Vars(r)["id"]
@ -996,7 +995,7 @@ func (api *RestApi) deleteJobById(rw http.ResponseWriter, r *http.Request) {
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: finding job failed: sql: no rows in result set"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/delete_job/ [delete]
// @router /api/jobs/delete_job/ [delete]
func (api *RestApi) deleteJobByRequest(rw http.ResponseWriter, r *http.Request) {
// Parse request body
req := DeleteJobApiRequest{}
@ -1046,7 +1045,7 @@ func (api *RestApi) deleteJobByRequest(rw http.ResponseWriter, r *http.Request)
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: finding job failed: sql: no rows in result set"
// @failure 500 {object} api.ErrorResponse "Internal Server Error"
// @security ApiKeyAuth
// @router /jobs/delete_job_before/{ts} [delete]
// @router /api/jobs/delete_job_before/{ts} [delete]
func (api *RestApi) deleteJobBefore(rw http.ResponseWriter, r *http.Request) {
var cnt int
// Fetch job (that will be stopped) from db
@ -1183,7 +1182,7 @@ func (api *RestApi) getJobMetrics(rw http.ResponseWriter, r *http.Request) {
// @failure 422 {string} string "Unprocessable Entity: creating user failed"
// @failure 500 {string} string "Internal Server Error"
// @security ApiKeyAuth
// @router /users/ [post]
// @router /config/users/ [post]
func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) {
// SecuredCheck() only worked with TokenAuth: Removed
@ -1258,7 +1257,7 @@ func (api *RestApi) deleteUser(rw http.ResponseWriter, r *http.Request) {
// @failure 403 {string} string "Forbidden"
// @failure 500 {string} string "Internal Server Error"
// @security ApiKeyAuth
// @router /users/ [get]
// @router /config/users/ [get]
func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) {
// SecuredCheck() only worked with TokenAuth: Removed