diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 9740d76..bf8cd75 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -65,7 +65,7 @@ func setup(t *testing.T) *api.RestApi { } ] }` - const testclusterJson = `{ + const testclusterJSON = `{ "name": "testcluster", "subClusters": [ { @@ -128,7 +128,7 @@ func setup(t *testing.T) *api.RestApi { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(jobarchive, "version.txt"), fmt.Appendf(nil, "%d", 2), 0o666); err != nil { + if err := os.WriteFile(filepath.Join(jobarchive, "version.txt"), fmt.Appendf(nil, "%d", 3), 0o666); err != nil { t.Fatal(err) } @@ -136,7 +136,7 @@ func setup(t *testing.T) *api.RestApi { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(jobarchive, "testcluster", "cluster.json"), []byte(testclusterJson), 0o666); err != nil { + if err := os.WriteFile(filepath.Join(jobarchive, "testcluster", "cluster.json"), []byte(testclusterJSON), 0o666); err != nil { t.Fatal(err) } diff --git a/internal/api/job.go b/internal/api/job.go index 3ec32ae..6b6f02e 100644 --- a/internal/api/job.go +++ b/internal/api/job.go @@ -2,6 +2,7 @@ // All rights reserved. This file is part of cc-backend. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. + package api import ( @@ -35,9 +36,9 @@ const ( secondsPerDay = 86400 ) -// StopJobApiRequest model -type StopJobApiRequest struct { - JobId *int64 `json:"jobId" example:"123000"` +// StopJobAPIRequest model +type StopJobAPIRequest struct { + JobID *int64 `json:"jobId" example:"123000"` Cluster *string `json:"cluster" example:"fritz"` StartTime *int64 `json:"startTime" example:"1649723812"` State schema.JobState `json:"jobState" validate:"required" example:"completed"` @@ -46,7 +47,7 @@ type StopJobApiRequest struct { // DeleteJobApiRequest model type DeleteJobApiRequest struct { - JobId *int64 `json:"jobId" validate:"required" example:"123000"` // Cluster Job ID of job + JobID *int64 `json:"jobId" validate:"required" example:"123000"` // Cluster Job ID of job Cluster *string `json:"cluster" example:"fritz"` // Cluster of job StartTime *int64 `json:"startTime" example:"1649723812"` // Start Time of job as epoch } @@ -740,7 +741,7 @@ func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) { // @router /api/jobs/stop_job/ [post] func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) { // Parse request body - req := StopJobApiRequest{} + req := StopJobAPIRequest{} if err := decode(r.Body, &req); err != nil { handleError(fmt.Errorf("parsing request body failed: %w", err), http.StatusBadRequest, rw) return @@ -749,16 +750,16 @@ func (api *RestApi) stopJobByRequest(rw http.ResponseWriter, r *http.Request) { // Fetch job (that will be stopped) from db var job *schema.Job var err error - if req.JobId == nil { + if req.JobID == nil { handleError(errors.New("the field 'jobId' is required"), http.StatusBadRequest, rw) return } // cclog.Printf("loading db job for stopJobByRequest... : stopJobApiRequest=%v", req) - job, err = api.JobRepository.Find(req.JobId, req.Cluster, req.StartTime) + job, err = api.JobRepository.Find(req.JobID, req.Cluster, req.StartTime) if err != nil { // Try cached jobs if not found in main repository - cachedJob, cachedErr := api.JobRepository.FindCached(req.JobId, req.Cluster, req.StartTime) + cachedJob, cachedErr := api.JobRepository.FindCached(req.JobID, req.Cluster, req.StartTime) if cachedErr != nil { // Combine both errors for better debugging handleError(fmt.Errorf("finding job failed: %w (cached lookup also failed: %v)", err, cachedErr), http.StatusNotFound, rw) @@ -841,12 +842,12 @@ func (api *RestApi) deleteJobByRequest(rw http.ResponseWriter, r *http.Request) // Fetch job (that will be deleted) from db var job *schema.Job var err error - if req.JobId == nil { + if req.JobID == nil { handleError(errors.New("the field 'jobId' is required"), http.StatusBadRequest, rw) return } - job, err = api.JobRepository.Find(req.JobId, req.Cluster, req.StartTime) + job, err = api.JobRepository.Find(req.JobID, req.Cluster, req.StartTime) if err != nil { handleError(fmt.Errorf("finding job failed: %w", err), http.StatusUnprocessableEntity, rw) return @@ -913,7 +914,7 @@ 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 if 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) diff --git a/internal/api/user.go b/internal/api/user.go index 85b4ba3..b56212c 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -2,6 +2,7 @@ // All rights reserved. This file is part of cc-backend. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. + package api import ( @@ -10,11 +11,12 @@ import ( "net/http" "github.com/ClusterCockpit/cc-backend/internal/repository" + cclog "github.com/ClusterCockpit/cc-lib/ccLogger" "github.com/ClusterCockpit/cc-lib/schema" "github.com/gorilla/mux" ) -type ApiReturnedUser struct { +type APIReturnedUser struct { Username string `json:"username"` Name string `json:"name"` Roles []string `json:"roles"` diff --git a/internal/importer/importer_test.go b/internal/importer/importer_test.go index ff28619..d6bfd94 100644 --- a/internal/importer/importer_test.go +++ b/internal/importer/importer_test.go @@ -81,14 +81,14 @@ func setup(t *testing.T) *repository.JobRepository { tmpdir := t.TempDir() jobarchive := filepath.Join(tmpdir, "job-archive") - if err := os.Mkdir(jobarchive, 0777); err != nil { + if err := os.Mkdir(jobarchive, 0o777); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(jobarchive, "version.txt"), []byte(fmt.Sprintf("%d", 2)), 0666); err != nil { + if err := os.WriteFile(filepath.Join(jobarchive, "version.txt"), fmt.Appendf(nil, "%d", 3), 0o666); err != nil { t.Fatal(err) } fritzArchive := filepath.Join(tmpdir, "job-archive", "fritz") - if err := os.Mkdir(fritzArchive, 0777); err != nil { + if err := os.Mkdir(fritzArchive, 0o777); err != nil { t.Fatal(err) } if err := copyFile(filepath.Join("testdata", "cluster-fritz.json"), @@ -103,7 +103,7 @@ func setup(t *testing.T) *repository.JobRepository { } cfgFilePath := filepath.Join(tmpdir, "config.json") - if err := os.WriteFile(cfgFilePath, []byte(testconfig), 0666); err != nil { + if err := os.WriteFile(cfgFilePath, []byte(testconfig), 0o666); err != nil { t.Fatal(err) } diff --git a/internal/repository/config.go b/internal/repository/config.go index b54d0ae..114702f 100644 --- a/internal/repository/config.go +++ b/internal/repository/config.go @@ -2,6 +2,7 @@ // All rights reserved. This file is part of cc-backend. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. + package repository import "time" @@ -39,12 +40,12 @@ type RepositoryConfig struct { // These values are optimized for typical deployments. func DefaultConfig() *RepositoryConfig { return &RepositoryConfig{ - CacheSize: 1 * 1024 * 1024, // 1MB - MaxOpenConnections: 4, - MaxIdleConnections: 4, - ConnectionMaxLifetime: time.Hour, - ConnectionMaxIdleTime: time.Hour, - MinRunningJobDuration: 600, // 10 minutes + CacheSize: 1 * 1024 * 1024, // 1MB + MaxOpenConnections: 4, + MaxIdleConnections: 4, + ConnectionMaxLifetime: time.Hour, + ConnectionMaxIdleTime: time.Hour, + MinRunningJobDuration: 600, // 10 minutes } } diff --git a/internal/repository/dbConnection.go b/internal/repository/dbConnection.go index 79de284..1c14c95 100644 --- a/internal/repository/dbConnection.go +++ b/internal/repository/dbConnection.go @@ -2,6 +2,7 @@ // All rights reserved. This file is part of cc-backend. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. + package repository import ( @@ -35,15 +36,15 @@ type DatabaseOptions struct { ConnectionMaxIdleTime time.Duration } -func setupSqlite(db *sql.DB) (err error) { +func setupSqlite(db *sql.DB) error { pragmas := []string{ "temp_store = memory", } for _, pragma := range pragmas { - _, err = db.Exec("PRAGMA " + pragma) + _, err := db.Exec("PRAGMA " + pragma) if err != nil { - return + return err } } @@ -67,14 +68,14 @@ func Connect(driver string, db string) { case "sqlite3": // TODO: Have separate DB handles for Writes and Reads // Optimize SQLite connection: https://kerkour.com/sqlite-for-servers - connectionUrlParams := make(url.Values) - connectionUrlParams.Add("_txlock", "immediate") - connectionUrlParams.Add("_journal_mode", "WAL") - connectionUrlParams.Add("_busy_timeout", "5000") - connectionUrlParams.Add("_synchronous", "NORMAL") - connectionUrlParams.Add("_cache_size", "1000000000") - connectionUrlParams.Add("_foreign_keys", "true") - opts.URL = fmt.Sprintf("file:%s?%s", opts.URL, connectionUrlParams.Encode()) + connectionURLParams := make(url.Values) + connectionURLParams.Add("_txlock", "immediate") + connectionURLParams.Add("_journal_mode", "WAL") + connectionURLParams.Add("_busy_timeout", "5000") + connectionURLParams.Add("_synchronous", "NORMAL") + connectionURLParams.Add("_cache_size", "1000000000") + connectionURLParams.Add("_foreign_keys", "true") + opts.URL = fmt.Sprintf("file:%s?%s", opts.URL, connectionURLParams.Encode()) if cclog.Loglevel() == "debug" { sql.Register("sqlite3WithHooks", sqlhooks.Wrap(&sqlite3.SQLiteDriver{}, &Hooks{})) @@ -83,7 +84,10 @@ func Connect(driver string, db string) { dbHandle, err = sqlx.Open("sqlite3", opts.URL) } - setupSqlite(dbHandle.DB) + err = setupSqlite(dbHandle.DB) + if err != nil { + cclog.Abortf("Failed sqlite db setup.\nError: %s\n", err.Error()) + } case "mysql": opts.URL += "?multiStatements=true" dbHandle, err = sqlx.Open("mysql", opts.URL) diff --git a/internal/repository/node_test.go b/internal/repository/node_test.go index 4ff5870..b42e09b 100644 --- a/internal/repository/node_test.go +++ b/internal/repository/node_test.go @@ -115,7 +115,7 @@ func nodeTestSetup(t *testing.T) { } if err := os.WriteFile(filepath.Join(jobarchive, "version.txt"), - fmt.Appendf(nil, "%d", 2), 0o666); err != nil { + fmt.Appendf(nil, "%d", 3), 0o666); err != nil { t.Fatal(err) } diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index 435c34f..bfcb38a 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -92,7 +92,7 @@ import ( // Version is the current archive schema version. // The archive backend must match this version for compatibility. -const Version uint64 = 2 +const Version uint64 = 3 // ArchiveBackend defines the interface that all archive storage backends must implement. // Implementations include FsArchive (filesystem), S3Archive (object storage), and SqliteArchive (database). @@ -170,8 +170,8 @@ type ArchiveBackend interface { // JobContainer combines job metadata and optional performance data. // Used by Iter() to yield jobs during archive iteration. type JobContainer struct { - Meta *schema.Job // Job metadata (always present) - Data *schema.JobData // Performance data (nil if not loaded) + Meta *schema.Job // Job metadata (always present) + Data *schema.JobData // Performance data (nil if not loaded) } var ( diff --git a/pkg/archive/fsBackend_test.go b/pkg/archive/fsBackend_test.go index c872d0a..a43a6c3 100644 --- a/pkg/archive/fsBackend_test.go +++ b/pkg/archive/fsBackend_test.go @@ -47,7 +47,7 @@ func TestInit(t *testing.T) { if fsa.path != "testdata/archive" { t.Fail() } - if version != 2 { + if version != 3 { t.Fail() } if len(fsa.clusters) != 3 || fsa.clusters[1] != "emmy" { diff --git a/pkg/archive/testdata/archive/version.txt b/pkg/archive/testdata/archive/version.txt index 0cfbf08..00750ed 100644 --- a/pkg/archive/testdata/archive/version.txt +++ b/pkg/archive/testdata/archive/version.txt @@ -1 +1 @@ -2 +3