feat: 添加 Job 领域类型和 JobsService
约 93 个结构体覆盖 JobInfo、JobDescMsg、SlurmDBD Job/Step/QOS/Account/User 等类型。JobsService 提供 GetJobs、GetJob、PostJob、DeleteJob、SubmitJob 5 个方法,并定义 JobFlag* 和 JobSignalFlag* 常量。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
172
internal/slurm/slurm_jobs.go
Normal file
172
internal/slurm/slurm_jobs.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package slurm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Job query flags for GetJobsOptions.Flags and GetNodeOptions.Flags.
|
||||
const (
|
||||
JobFlagAll = "ALL"
|
||||
JobFlagDetail = "DETAIL"
|
||||
JobFlagMixed = "MIXED"
|
||||
JobFlagLocal = "LOCAL"
|
||||
JobFlagSibling = "SIBLING"
|
||||
JobFlagFederation = "FEDERATION"
|
||||
JobFlagFuture = "FUTURE"
|
||||
)
|
||||
|
||||
// GetJobsOptions specifies optional parameters for GetJobs.
|
||||
type GetJobsOptions struct {
|
||||
UpdateTime *string `url:"update_time,omitempty"`
|
||||
Flags *string `url:"flags,omitempty"` // Use JobFlag* constants (e.g. JobFlagDetail)
|
||||
}
|
||||
|
||||
// Job signal flags for DeleteJobOptions.Flags.
|
||||
const (
|
||||
JobSignalFlagBatchJob = "BATCH_JOB"
|
||||
JobSignalFlagArrayTask = "ARRAY_TASK"
|
||||
JobSignalFlagFullStepsOnly = "FULL_STEPS_ONLY"
|
||||
JobSignalFlagFullJob = "FULL_JOB"
|
||||
JobSignalFlagFederationRequeue = "FEDERATION_REQUEUE"
|
||||
JobSignalFlagHurry = "HURRY"
|
||||
JobSignalFlagOutOfMemory = "OUT_OF_MEMORY"
|
||||
JobSignalFlagNoSiblingJobs = "NO_SIBLING_JOBS"
|
||||
JobSignalFlagReservationJob = "RESERVATION_JOB"
|
||||
JobSignalFlagWarningSent = "WARNING_SENT"
|
||||
)
|
||||
|
||||
// DeleteJobOptions specifies optional parameters for DeleteJob.
|
||||
type DeleteJobOptions struct {
|
||||
Signal *string `url:"signal,omitempty"`
|
||||
Flags *string `url:"flags,omitempty"` // Use JobSignalFlag* constants (e.g. JobSignalFlagHurry)
|
||||
}
|
||||
|
||||
// GetJobs lists all jobs.
|
||||
func (s *JobsService) GetJobs(ctx context.Context, opts *GetJobsOptions) (*OpenapiJobInfoResp, *Response, error) {
|
||||
path := "slurm/v0.0.40/jobs"
|
||||
req, err := s.client.NewRequest("GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
u, parseErr := url.Parse(req.URL.String())
|
||||
if parseErr != nil {
|
||||
return nil, nil, parseErr
|
||||
}
|
||||
q := u.Query()
|
||||
if opts.UpdateTime != nil {
|
||||
q.Set("update_time", *opts.UpdateTime)
|
||||
}
|
||||
if opts.Flags != nil {
|
||||
q.Set("flags", *opts.Flags)
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
req.URL = u
|
||||
}
|
||||
|
||||
var result OpenapiJobInfoResp
|
||||
resp, err := s.client.Do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return &result, resp, nil
|
||||
}
|
||||
|
||||
// GetJob gets a single job by ID.
|
||||
func (s *JobsService) GetJob(ctx context.Context, jobID string, opts *GetJobsOptions) (*OpenapiJobInfoResp, *Response, error) {
|
||||
path := fmt.Sprintf("slurm/v0.0.40/job/%s", jobID)
|
||||
req, err := s.client.NewRequest("GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
u, parseErr := url.Parse(req.URL.String())
|
||||
if parseErr != nil {
|
||||
return nil, nil, parseErr
|
||||
}
|
||||
q := u.Query()
|
||||
if opts.UpdateTime != nil {
|
||||
q.Set("update_time", *opts.UpdateTime)
|
||||
}
|
||||
if opts.Flags != nil {
|
||||
q.Set("flags", *opts.Flags)
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
req.URL = u
|
||||
}
|
||||
|
||||
var result OpenapiJobInfoResp
|
||||
resp, err := s.client.Do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return &result, resp, nil
|
||||
}
|
||||
|
||||
// PostJob updates a job.
|
||||
func (s *JobsService) PostJob(ctx context.Context, jobID string, desc *JobDescMsg) (*OpenapiJobPostResponse, *Response, error) {
|
||||
path := fmt.Sprintf("slurm/v0.0.40/job/%s", jobID)
|
||||
req, err := s.client.NewRequest("POST", path, desc)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var result OpenapiJobPostResponse
|
||||
resp, err := s.client.Do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return &result, resp, nil
|
||||
}
|
||||
|
||||
// DeleteJob cancels (signals) a job.
|
||||
func (s *JobsService) DeleteJob(ctx context.Context, jobID string, opts *DeleteJobOptions) (*OpenapiResp, *Response, error) {
|
||||
path := fmt.Sprintf("slurm/v0.0.40/job/%s", jobID)
|
||||
req, err := s.client.NewRequest("DELETE", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
u, parseErr := url.Parse(req.URL.String())
|
||||
if parseErr != nil {
|
||||
return nil, nil, parseErr
|
||||
}
|
||||
q := u.Query()
|
||||
if opts.Signal != nil {
|
||||
q.Set("signal", *opts.Signal)
|
||||
}
|
||||
if opts.Flags != nil {
|
||||
q.Set("flags", *opts.Flags)
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
req.URL = u
|
||||
}
|
||||
|
||||
var result OpenapiResp
|
||||
resp, err := s.client.Do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return &result, resp, nil
|
||||
}
|
||||
|
||||
// SubmitJob submits a new job.
|
||||
func (s *JobsService) SubmitJob(ctx context.Context, reqBody *JobSubmitReq) (*OpenapiJobSubmitResponse, *Response, error) {
|
||||
path := "slurm/v0.0.40/job/submit"
|
||||
req, err := s.client.NewRequest("POST", path, reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var result OpenapiJobSubmitResponse
|
||||
resp, err := s.client.Do(ctx, req, &result)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return &result, resp, nil
|
||||
}
|
||||
286
internal/slurm/slurm_jobs_test.go
Normal file
286
internal/slurm/slurm_jobs_test.go
Normal file
@@ -0,0 +1,286 @@
|
||||
package slurm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testMethod(t *testing.T, r *http.Request, expected string) {
|
||||
t.Helper()
|
||||
if r.Method != expected {
|
||||
t.Errorf("expected method %s, got %s", expected, r.Method)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_GetJobs(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
fmt.Fprint(w, `{"jobs": [], "last_backfill": {}, "last_update": {}}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
resp, _, err := client.Jobs.GetJobs(context.Background(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
if len(resp.Jobs) != 0 {
|
||||
t.Errorf("expected empty jobs, got %d", len(resp.Jobs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_GetJobs_WithOptions(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
q := r.URL.Query()
|
||||
if q.Get("update_time") != "12345" {
|
||||
t.Errorf("expected update_time=12345, got %s", q.Get("update_time"))
|
||||
}
|
||||
if q.Get("flags") != JobFlagDetail {
|
||||
t.Errorf("expected flags=%s, got %s", JobFlagDetail, q.Get("flags"))
|
||||
}
|
||||
fmt.Fprint(w, `{"jobs": [], "last_update": {}}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
opts := &GetJobsOptions{
|
||||
UpdateTime: Ptr("12345"),
|
||||
Flags: Ptr(JobFlagDetail),
|
||||
}
|
||||
resp, _, err := client.Jobs.GetJobs(context.Background(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_GetJob(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
fmt.Fprint(w, `{"jobs": [{"job_id": 42, "name": "test-job"}], "last_update": {}}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
resp, _, err := client.Jobs.GetJob(context.Background(), "42", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
if len(resp.Jobs) != 1 {
|
||||
t.Fatalf("expected 1 job, got %d", len(resp.Jobs))
|
||||
}
|
||||
if resp.Jobs[0].JobID == nil || *resp.Jobs[0].JobID != 42 {
|
||||
t.Errorf("expected job_id=42, got %v", resp.Jobs[0].JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_PostJob(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "POST")
|
||||
if ct := r.Header.Get("Content-Type"); ct != "application/json" {
|
||||
t.Errorf("expected Content-Type application/json, got %s", ct)
|
||||
}
|
||||
fmt.Fprint(w, `{"job_id": "42", "step_id": "0", "results": []}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
desc := &JobDescMsg{
|
||||
Name: Ptr("updated-job"),
|
||||
}
|
||||
resp, _, err := client.Jobs.PostJob(context.Background(), "42", desc)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
if resp.JobID == nil || *resp.JobID != "42" {
|
||||
t.Errorf("expected job_id=42, got %v", resp.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_DeleteJob(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "DELETE")
|
||||
q := r.URL.Query()
|
||||
if q.Get("signal") != "SIGTERM" {
|
||||
t.Errorf("expected signal=SIGTERM, got %s", q.Get("signal"))
|
||||
}
|
||||
fmt.Fprint(w, `{}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
opts := &DeleteJobOptions{
|
||||
Signal: Ptr("SIGTERM"),
|
||||
}
|
||||
resp, _, err := client.Jobs.DeleteJob(context.Background(), "42", opts)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_SubmitJob(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/job/submit", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "POST")
|
||||
if ct := r.Header.Get("Content-Type"); ct != "application/json" {
|
||||
t.Errorf("expected Content-Type application/json, got %s", ct)
|
||||
}
|
||||
fmt.Fprint(w, `{"job_id": 100, "step_id": "0", "result": {"job_id": 100, "step_id": "0"}}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
reqBody := &JobSubmitReq{
|
||||
Script: Ptr("#!/bin/bash\necho hello"),
|
||||
Job: &JobDescMsg{
|
||||
Name: Ptr("test-submit"),
|
||||
},
|
||||
}
|
||||
resp, _, err := client.Jobs.SubmitJob(context.Background(), reqBody)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
if resp.JobID == nil || *resp.JobID != 100 {
|
||||
t.Errorf("expected job_id=100, got %v", resp.JobID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_GetJobs_Error(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, `{"errors": [{"error": "internal error"}]}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
_, _, err := client.Jobs.GetJobs(context.Background(), nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for 500 response")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "500") {
|
||||
t.Errorf("expected error to contain 500, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_GetJob_Error(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/job/999", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, `{"errors": [{"error": "job not found"}]}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
_, _, err := client.Jobs.GetJob(context.Background(), "999", nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for 404 response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_DeleteJob_NoOptions(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/job/42", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "DELETE")
|
||||
q := r.URL.Query()
|
||||
if len(q) != 0 {
|
||||
t.Errorf("expected no query params, got %v", q)
|
||||
}
|
||||
fmt.Fprint(w, `{}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
resp, _, err := client.Jobs.DeleteJob(context.Background(), "42", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_GetJobPathEscaping(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/job/", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
parts := strings.Split(r.URL.Path, "/")
|
||||
jobID := parts[len(parts)-1]
|
||||
if jobID != "123" {
|
||||
t.Errorf("expected job ID 123, got %s", jobID)
|
||||
}
|
||||
fmt.Fprint(w, `{"jobs": [{"job_id": 123}]}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
resp, _, err := client.Jobs.GetJob(context.Background(), "123", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobsService_GetJobsWithURLQuery(t *testing.T) {
|
||||
var capturedQuery url.Values
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/slurm/v0.0.40/jobs", func(w http.ResponseWriter, r *http.Request) {
|
||||
capturedQuery = r.URL.Query()
|
||||
testMethod(t, r, "GET")
|
||||
fmt.Fprint(w, `{"jobs": []}`)
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
client, _ := NewClient(server.URL, nil)
|
||||
opts := &GetJobsOptions{
|
||||
UpdateTime: Ptr("1700000000"),
|
||||
}
|
||||
_, _, err := client.Jobs.GetJobs(context.Background(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if capturedQuery.Get("update_time") != "1700000000" {
|
||||
t.Errorf("expected update_time=1700000000, got %s", capturedQuery.Get("update_time"))
|
||||
}
|
||||
}
|
||||
1149
internal/slurm/types_job.go
Normal file
1149
internal/slurm/types_job.go
Normal file
File diff suppressed because it is too large
Load Diff
777
internal/slurm/types_job_test.go
Normal file
777
internal/slurm/types_job_test.go
Normal file
@@ -0,0 +1,777 @@
|
||||
package slurm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJobInfoRoundTrip(t *testing.T) {
|
||||
orig := JobInfo{
|
||||
Account: Ptr("testacct"),
|
||||
AccrueTime: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1234567890))},
|
||||
ArrayJobID: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(100))},
|
||||
ArrayTaskID: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(1))},
|
||||
BatchFlag: Ptr(true),
|
||||
BatchHost: Ptr("node01"),
|
||||
Cluster: Ptr("test-cluster"),
|
||||
Command: Ptr("/usr/bin/true"),
|
||||
Contiguous: Ptr(false),
|
||||
Cpus: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(4))},
|
||||
ExitCode: &ProcessExitCodeVerbose{
|
||||
Status: []string{"EXITED"},
|
||||
ReturnCode: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(0))},
|
||||
},
|
||||
Flags: []string{"CRON_JOB"},
|
||||
GroupID: Ptr(int32(1000)),
|
||||
JobID: Ptr(int32(12345)),
|
||||
JobState: []string{"COMPLETED"},
|
||||
Name: Ptr("test-job"),
|
||||
NodeCount: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(2))},
|
||||
Nodes: Ptr("node[01-02]"),
|
||||
Partition: Ptr("normal"),
|
||||
Priority: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(4294901758))},
|
||||
StartTime: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1234567890))},
|
||||
SubmitTime: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1234567800))},
|
||||
Tasks: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(2))},
|
||||
TimeLimit: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(60))},
|
||||
UserID: Ptr(int32(1001)),
|
||||
UserName: Ptr("testuser"),
|
||||
CurrentWorkingDirectory: Ptr("/home/testuser"),
|
||||
Power: &JobInfoPower{Flags: []string{"EQUAL_POWER"}},
|
||||
StandardOutput: Ptr("/home/testuser/slurm-%j.out"),
|
||||
StandardError: Ptr("/home/testuser/slurm-%j.err"),
|
||||
StandardInput: Ptr("/dev/null"),
|
||||
GresDetail: JobInfoGresDetail{"gpu:2", "mps:100"},
|
||||
BillableTres: &Float64NoVal{Set: Ptr(true), Number: Ptr(4.0)},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded JobInfo
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Account == nil || *decoded.Account != "testacct" {
|
||||
t.Fatalf("account mismatch: %v", decoded.Account)
|
||||
}
|
||||
if decoded.JobID == nil || *decoded.JobID != 12345 {
|
||||
t.Fatalf("job_id mismatch: %v", decoded.JobID)
|
||||
}
|
||||
if len(decoded.JobState) != 1 || decoded.JobState[0] != "COMPLETED" {
|
||||
t.Fatalf("job_state mismatch: %v", decoded.JobState)
|
||||
}
|
||||
if decoded.Cpus == nil || decoded.Cpus.Number == nil || *decoded.Cpus.Number != 4 {
|
||||
t.Fatalf("cpus mismatch: %v", decoded.Cpus)
|
||||
}
|
||||
if decoded.ExitCode == nil || decoded.ExitCode.ReturnCode == nil {
|
||||
t.Fatal("exit_code should not be nil")
|
||||
}
|
||||
if decoded.Power == nil || len(decoded.Power.Flags) != 1 || decoded.Power.Flags[0] != "EQUAL_POWER" {
|
||||
t.Fatalf("power mismatch: %v", decoded.Power)
|
||||
}
|
||||
if len(decoded.GresDetail) != 2 || decoded.GresDetail[0] != "gpu:2" {
|
||||
t.Fatalf("gres_detail mismatch: %v", decoded.GresDetail)
|
||||
}
|
||||
if decoded.BillableTres == nil || decoded.BillableTres.Number == nil || *decoded.BillableTres.Number != 4.0 {
|
||||
t.Fatalf("billable_tres mismatch: %v", decoded.BillableTres)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobDescMsgRoundTrip(t *testing.T) {
|
||||
orig := JobDescMsg{
|
||||
Script: Ptr("#!/bin/bash\necho hello"),
|
||||
Name: Ptr("submit-test"),
|
||||
Account: Ptr("testacct"),
|
||||
Partition: Ptr("normal"),
|
||||
Tasks: Ptr(int32(4)),
|
||||
Nodes: Ptr("node[01-02]"),
|
||||
TimeLimit: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(30))},
|
||||
MemoryPerCpu: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(4096))},
|
||||
MemoryPerNode: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(8192))},
|
||||
Argv: StringArray{"/bin/bash", "-c", "echo hello"},
|
||||
Environment: StringArray{"PATH=/usr/bin", "HOME=/root"},
|
||||
CurrentWorkingDirectory: Ptr("/tmp"),
|
||||
StandardOutput: Ptr("slurm-%j.out"),
|
||||
Flags: []string{"SPREAD_JOB"},
|
||||
MailType: []string{"BEGIN", "END"},
|
||||
MailUser: Ptr("user@example.com"),
|
||||
Rlimits: &JobDescRlimits{
|
||||
Cpu: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(3600))},
|
||||
Nofile: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(65536))},
|
||||
},
|
||||
Crontab: &CronEntry{
|
||||
Minute: Ptr("*/5"),
|
||||
Hour: Ptr("*"),
|
||||
Line: &CronEntryLine{Start: Ptr(int32(1)), End: Ptr(int32(5))},
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded JobDescMsg
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Script == nil || *decoded.Script != "#!/bin/bash\necho hello" {
|
||||
t.Fatalf("script mismatch")
|
||||
}
|
||||
if decoded.Name == nil || *decoded.Name != "submit-test" {
|
||||
t.Fatalf("name mismatch")
|
||||
}
|
||||
if decoded.Tasks == nil || *decoded.Tasks != 4 {
|
||||
t.Fatalf("tasks mismatch: %v", decoded.Tasks)
|
||||
}
|
||||
if decoded.Rlimits == nil || decoded.Rlimits.Cpu == nil {
|
||||
t.Fatal("rlimits should not be nil")
|
||||
}
|
||||
if *decoded.Rlimits.Cpu.Number != 3600 {
|
||||
t.Fatalf("rlimits.cpu mismatch: %v", decoded.Rlimits.Cpu)
|
||||
}
|
||||
if decoded.Crontab == nil || decoded.Crontab.Minute == nil || *decoded.Crontab.Minute != "*/5" {
|
||||
t.Fatal("crontab.minute mismatch")
|
||||
}
|
||||
if len(decoded.Argv) != 3 {
|
||||
t.Fatalf("argv mismatch: %v", decoded.Argv)
|
||||
}
|
||||
if len(decoded.Flags) != 1 || decoded.Flags[0] != "SPREAD_JOB" {
|
||||
t.Fatalf("flags mismatch: %v", decoded.Flags)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobSubmitReqRoundTrip(t *testing.T) {
|
||||
orig := JobSubmitReq{
|
||||
Script: Ptr("#!/bin/bash\necho test"),
|
||||
Job: &JobDescMsg{
|
||||
Name: Ptr("myjob"),
|
||||
Partition: Ptr("debug"),
|
||||
Tasks: Ptr(int32(1)),
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded JobSubmitReq
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Script == nil || *decoded.Script != "#!/bin/bash\necho test" {
|
||||
t.Fatal("script mismatch")
|
||||
}
|
||||
if decoded.Job == nil || decoded.Job.Name == nil || *decoded.Job.Name != "myjob" {
|
||||
t.Fatal("job.name mismatch")
|
||||
}
|
||||
if decoded.Job.Tasks == nil || *decoded.Job.Tasks != 1 {
|
||||
t.Fatal("job.tasks mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenapiJobInfoRespRoundTrip(t *testing.T) {
|
||||
orig := OpenapiJobInfoResp{
|
||||
Jobs: JobInfoMsg{
|
||||
{
|
||||
JobID: Ptr(int32(100)),
|
||||
Name: Ptr("job-a"),
|
||||
JobState: []string{"RUNNING"},
|
||||
},
|
||||
{
|
||||
JobID: Ptr(int32(101)),
|
||||
Name: Ptr("job-b"),
|
||||
JobState: []string{"PENDING"},
|
||||
},
|
||||
},
|
||||
LastBackfill: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1234567890))},
|
||||
LastUpdate: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1234567900))},
|
||||
Meta: &OpenapiMeta{
|
||||
Slurm: &MetaSlurm{
|
||||
Version: &MetaSlurmVersion{
|
||||
Major: Ptr("24"),
|
||||
Micro: Ptr("5"),
|
||||
Minor: Ptr("05"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Errors: OpenapiErrors{{Error: Ptr("none")}},
|
||||
Warnings: OpenapiWarnings{},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded OpenapiJobInfoResp
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if len(decoded.Jobs) != 2 {
|
||||
t.Fatalf("expected 2 jobs, got %d", len(decoded.Jobs))
|
||||
}
|
||||
if decoded.Jobs[0].JobID == nil || *decoded.Jobs[0].JobID != 100 {
|
||||
t.Fatalf("job[0].job_id mismatch")
|
||||
}
|
||||
if decoded.Jobs[1].Name == nil || *decoded.Jobs[1].Name != "job-b" {
|
||||
t.Fatalf("job[1].name mismatch")
|
||||
}
|
||||
if decoded.Meta == nil || decoded.Meta.Slurm == nil || decoded.Meta.Slurm.Version == nil {
|
||||
t.Fatal("meta.slurm.version should not be nil")
|
||||
}
|
||||
if decoded.LastBackfill == nil || decoded.LastBackfill.Number == nil || *decoded.LastBackfill.Number != 1234567890 {
|
||||
t.Fatalf("last_backfill mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenapiJobPostResponseRoundTrip(t *testing.T) {
|
||||
orig := OpenapiJobPostResponse{
|
||||
Results: JobArrayResponseArray{
|
||||
{
|
||||
JobID: Ptr(int32(200)),
|
||||
StepID: Ptr("batch"),
|
||||
Error: Ptr(""),
|
||||
Why: Ptr("Job updated"),
|
||||
},
|
||||
},
|
||||
JobID: Ptr("200"),
|
||||
StepID: Ptr("batch"),
|
||||
JobSubmitUserMsg: Ptr("Job updated successfully"),
|
||||
Meta: &OpenapiMeta{
|
||||
Plugin: &MetaPlugin{Type: Ptr("type")},
|
||||
},
|
||||
Errors: OpenapiErrors{},
|
||||
Warnings: OpenapiWarnings{},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded OpenapiJobPostResponse
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if len(decoded.Results) != 1 {
|
||||
t.Fatalf("expected 1 result, got %d", len(decoded.Results))
|
||||
}
|
||||
if decoded.Results[0].JobID == nil || *decoded.Results[0].JobID != 200 {
|
||||
t.Fatalf("results[0].job_id mismatch")
|
||||
}
|
||||
if decoded.JobSubmitUserMsg == nil || *decoded.JobSubmitUserMsg != "Job updated successfully" {
|
||||
t.Fatal("job_submit_user_msg mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenapiJobSubmitResponseRoundTrip(t *testing.T) {
|
||||
orig := OpenapiJobSubmitResponse{
|
||||
Result: &JobSubmitResponseMsg{
|
||||
JobID: Ptr(int32(300)),
|
||||
StepID: Ptr("batch"),
|
||||
JobSubmitUserMsg: Ptr("Job submitted"),
|
||||
},
|
||||
JobID: Ptr(int32(300)),
|
||||
StepID: Ptr("batch"),
|
||||
JobSubmitUserMsg: Ptr("Job submitted"),
|
||||
Meta: &OpenapiMeta{
|
||||
Client: &MetaClient{User: Ptr("testuser")},
|
||||
},
|
||||
Errors: OpenapiErrors{{Description: Ptr("no error")}},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded OpenapiJobSubmitResponse
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Result == nil || decoded.Result.JobID == nil || *decoded.Result.JobID != 300 {
|
||||
t.Fatal("result.job_id mismatch")
|
||||
}
|
||||
if decoded.JobID == nil || *decoded.JobID != 300 {
|
||||
t.Fatal("job_id mismatch")
|
||||
}
|
||||
if decoded.Meta == nil || decoded.Meta.Client == nil || decoded.Meta.Client.User == nil {
|
||||
t.Fatal("meta.client.user should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenapiRespRoundTrip(t *testing.T) {
|
||||
orig := OpenapiResp{
|
||||
Meta: &OpenapiMeta{
|
||||
Slurm: &MetaSlurm{Release: Ptr("24.05.5")},
|
||||
},
|
||||
Errors: OpenapiErrors{},
|
||||
Warnings: OpenapiWarnings{},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded OpenapiResp
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Meta == nil || decoded.Meta.Slurm == nil || decoded.Meta.Slurm.Release == nil {
|
||||
t.Fatal("meta.slurm.release should not be nil")
|
||||
}
|
||||
if *decoded.Meta.Slurm.Release != "24.05.5" {
|
||||
t.Fatalf("release mismatch: %v", decoded.Meta.Slurm.Release)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobInfoMinimalRoundTrip(t *testing.T) {
|
||||
orig := JobInfo{JobID: Ptr(int32(1))}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded JobInfo
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.JobID == nil || *decoded.JobID != 1 {
|
||||
t.Fatal("job_id mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobInfoEmptyRoundTrip(t *testing.T) {
|
||||
orig := JobInfo{}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
if string(data) != "{}" {
|
||||
t.Fatalf("empty JobInfo should marshal to {}, got %s", data)
|
||||
}
|
||||
var decoded JobInfo
|
||||
if err := json.Unmarshal([]byte(`{}`), &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.JobID != nil {
|
||||
t.Fatal("job_id should be nil for empty object")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobResRoundTrip(t *testing.T) {
|
||||
orig := JobRes{
|
||||
Nodes: Ptr("node[01-02]"),
|
||||
AllocatedCores: Ptr(int32(8)),
|
||||
AllocatedCpus: Ptr(int32(16)),
|
||||
AllocatedHosts: Ptr(int32(2)),
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded JobRes
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.AllocatedCpus == nil || *decoded.AllocatedCpus != 16 {
|
||||
t.Fatal("allocated_cpus mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCronEntryRoundTrip(t *testing.T) {
|
||||
orig := CronEntry{
|
||||
Flags: []string{"WILD_MINUTE"},
|
||||
Minute: Ptr("0,30"),
|
||||
Hour: Ptr("*"),
|
||||
DayOfMonth: Ptr("*"),
|
||||
Month: Ptr("*"),
|
||||
DayOfWeek: Ptr("1-5"),
|
||||
Specification: Ptr("0,30 * * * 1-5"),
|
||||
Command: Ptr("/usr/bin/run-job"),
|
||||
Line: &CronEntryLine{Start: Ptr(int32(10)), End: Ptr(int32(12))},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded CronEntry
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Specification == nil || *decoded.Specification != "0,30 * * * 1-5" {
|
||||
t.Fatal("specification mismatch")
|
||||
}
|
||||
if decoded.Line == nil || decoded.Line.Start == nil || *decoded.Line.Start != 10 {
|
||||
t.Fatal("line.start mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTresRoundTrip(t *testing.T) {
|
||||
orig := Tres{
|
||||
Type: Ptr("cpu"),
|
||||
Name: Ptr(""),
|
||||
ID: Ptr(int32(1)),
|
||||
Count: Ptr(int64(4)),
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Tres
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Type == nil || *decoded.Type != "cpu" {
|
||||
t.Fatal("type mismatch")
|
||||
}
|
||||
if decoded.Count == nil || *decoded.Count != 4 {
|
||||
t.Fatal("count mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssocShortRoundTrip(t *testing.T) {
|
||||
orig := AssocShort{
|
||||
Account: Ptr("default"),
|
||||
Cluster: Ptr("test-cluster"),
|
||||
User: Ptr("testuser"),
|
||||
ID: Ptr(int32(42)),
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded AssocShort
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if *decoded.Account != "default" || *decoded.User != "testuser" || *decoded.ID != 42 {
|
||||
t.Fatal("round-trip mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQOSRoundTrip(t *testing.T) {
|
||||
orig := QOS{
|
||||
Name: Ptr("normal"),
|
||||
Description: Ptr("Default QOS"),
|
||||
ID: Ptr(int32(1)),
|
||||
Flags: []string{"NOT_SET"},
|
||||
Priority: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(100))},
|
||||
UsageFactor: &Float64NoVal{Set: Ptr(true), Number: Ptr(1.0)},
|
||||
Limits: &QOSLimits{
|
||||
GraceTime: Ptr(int32(30)),
|
||||
Max: &QOSLimitsMax{
|
||||
ActiveJobs: &QOSLimitsMaxActiveJobs{
|
||||
Count: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(100))},
|
||||
},
|
||||
WallClock: &QOSLimitsMaxWallClock{
|
||||
Per: &QOSLimitsMaxWallClockPer{
|
||||
Job: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(1440))},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Preempt: &QOSPreempt{
|
||||
Mode: []string{"REQUEUE"},
|
||||
ExemptTime: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(300))},
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded QOS
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Name == nil || *decoded.Name != "normal" {
|
||||
t.Fatal("name mismatch")
|
||||
}
|
||||
if decoded.Limits == nil || decoded.Limits.GraceTime == nil || *decoded.Limits.GraceTime != 30 {
|
||||
t.Fatal("limits.grace_time mismatch")
|
||||
}
|
||||
if decoded.Limits.Max == nil || decoded.Limits.Max.ActiveJobs == nil {
|
||||
t.Fatal("limits.max.active_jobs should not be nil")
|
||||
}
|
||||
if decoded.Preempt == nil || len(decoded.Preempt.Mode) != 1 || decoded.Preempt.Mode[0] != "REQUEUE" {
|
||||
t.Fatal("preempt.mode mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserRoundTrip(t *testing.T) {
|
||||
orig := User{
|
||||
Name: Ptr("testuser"),
|
||||
AdministratorLevel: []string{"None"},
|
||||
Associations: AssocShortList{
|
||||
{Account: Ptr("default"), Cluster: Ptr("test"), User: Ptr("testuser")},
|
||||
},
|
||||
Default: &UserDefault{
|
||||
Account: Ptr("default"),
|
||||
Wckey: Ptr("*"),
|
||||
},
|
||||
Flags: []string{"NONE"},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded User
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Name == nil || *decoded.Name != "testuser" {
|
||||
t.Fatal("name mismatch")
|
||||
}
|
||||
if len(decoded.Associations) != 1 {
|
||||
t.Fatalf("expected 1 association, got %d", len(decoded.Associations))
|
||||
}
|
||||
if decoded.Default == nil || decoded.Default.Account == nil || *decoded.Default.Account != "default" {
|
||||
t.Fatal("default.account mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountRoundTrip(t *testing.T) {
|
||||
orig := Account{
|
||||
Name: Ptr("testacct"),
|
||||
Description: Ptr("Test account"),
|
||||
Organization: Ptr("testorg"),
|
||||
Coordinators: CoordList{
|
||||
{Name: Ptr("admin1"), Direct: Ptr(true)},
|
||||
},
|
||||
Associations: AssocShortList{
|
||||
{Account: Ptr("testacct"), User: Ptr("user1")},
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Account
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Name == nil || *decoded.Name != "testacct" {
|
||||
t.Fatal("name mismatch")
|
||||
}
|
||||
if len(decoded.Coordinators) != 1 {
|
||||
t.Fatalf("expected 1 coordinator, got %d", len(decoded.Coordinators))
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterRecRoundTrip(t *testing.T) {
|
||||
orig := ClusterRec{
|
||||
Name: Ptr("test-cluster"),
|
||||
Controller: &ClusterRecController{
|
||||
Host: Ptr("ctrl-node"),
|
||||
Port: Ptr(int32(6817)),
|
||||
},
|
||||
Flags: []string{"MULTIPLE_SLURMD"},
|
||||
Tres: TresList{
|
||||
{Type: Ptr("cpu"), Count: Ptr(int64(100))},
|
||||
{Type: Ptr("mem"), Count: Ptr(int64(512000))},
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded ClusterRec
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Name == nil || *decoded.Name != "test-cluster" {
|
||||
t.Fatal("name mismatch")
|
||||
}
|
||||
if decoded.Controller == nil || decoded.Controller.Port == nil || *decoded.Controller.Port != 6817 {
|
||||
t.Fatal("controller.port mismatch")
|
||||
}
|
||||
if len(decoded.Tres) != 2 {
|
||||
t.Fatalf("expected 2 tres, got %d", len(decoded.Tres))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssocRoundTrip(t *testing.T) {
|
||||
orig := Assoc{
|
||||
Account: Ptr("testacct"),
|
||||
Cluster: Ptr("test-cluster"),
|
||||
User: Ptr("testuser"),
|
||||
ID: &AssocShort{
|
||||
Account: Ptr("testacct"),
|
||||
Cluster: Ptr("test-cluster"),
|
||||
User: Ptr("testuser"),
|
||||
ID: Ptr(int32(1)),
|
||||
},
|
||||
Default: &AssocDefault{Qos: Ptr("normal")},
|
||||
IsDefault: Ptr(true),
|
||||
Qos: QosStringIdList{"normal", "high"},
|
||||
Max: &AssocMax{
|
||||
Jobs: &AssocMaxJobs{
|
||||
Total: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(500))},
|
||||
},
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Assoc
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if *decoded.Account != "testacct" {
|
||||
t.Fatal("account mismatch")
|
||||
}
|
||||
if len(decoded.Qos) != 2 || decoded.Qos[0] != "normal" {
|
||||
t.Fatalf("qos mismatch: %v", decoded.Qos)
|
||||
}
|
||||
if decoded.Max == nil || decoded.Max.Jobs == nil {
|
||||
t.Fatal("max.jobs should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWckeyRoundTrip(t *testing.T) {
|
||||
orig := Wckey{
|
||||
Name: Ptr("mykey"),
|
||||
Cluster: Ptr("test-cluster"),
|
||||
User: Ptr("testuser"),
|
||||
ID: Ptr(int32(1)),
|
||||
Flags: []string{"DELETED"},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Wckey
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if *decoded.Name != "mykey" {
|
||||
t.Fatal("name mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceRoundTrip(t *testing.T) {
|
||||
orig := Instance{
|
||||
Cluster: Ptr("test-cluster"),
|
||||
InstanceID: Ptr("i-12345"),
|
||||
InstanceType: Ptr("c5.large"),
|
||||
NodeName: Ptr("node01"),
|
||||
Time: &InstanceTime{
|
||||
TimeStart: Ptr(int64(1234567890)),
|
||||
TimeEnd: Ptr(int64(1234567900)),
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Instance
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if *decoded.InstanceID != "i-12345" {
|
||||
t.Fatal("instance_id mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWckeyTagStructRoundTrip(t *testing.T) {
|
||||
orig := WckeyTagStruct{
|
||||
Wckey: Ptr("mykey"),
|
||||
Flags: []string{"ASSIGNED_DEFAULT"},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded WckeyTagStruct
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if *decoded.Wckey != "mykey" {
|
||||
t.Fatal("wckey mismatch")
|
||||
}
|
||||
if len(decoded.Flags) != 1 || decoded.Flags[0] != "ASSIGNED_DEFAULT" {
|
||||
t.Fatal("flags mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountingRoundTrip(t *testing.T) {
|
||||
orig := Accounting{
|
||||
ID: Ptr(int32(1)),
|
||||
Start: Ptr(int64(1234567890)),
|
||||
Allocated: &AccountingAllocated{
|
||||
Seconds: Ptr(int64(3600)),
|
||||
},
|
||||
TRES: &Tres{Type: Ptr("cpu"), Count: Ptr(int64(4))},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Accounting
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if *decoded.ID != 1 || *decoded.Start != 1234567890 {
|
||||
t.Fatal("id/start mismatch")
|
||||
}
|
||||
if decoded.Allocated == nil || *decoded.Allocated.Seconds != 3600 {
|
||||
t.Fatal("allocated.seconds mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoordRoundTrip(t *testing.T) {
|
||||
orig := Coord{
|
||||
Name: Ptr("admin"),
|
||||
Direct: Ptr(true),
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Coord
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if *decoded.Name != "admin" || !*decoded.Direct {
|
||||
t.Fatal("round-trip mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepRoundTrip(t *testing.T) {
|
||||
orig := Step{
|
||||
Step: &StepIDName{
|
||||
ID: Ptr("0"),
|
||||
Name: Ptr("step0"),
|
||||
},
|
||||
State: []string{"COMPLETED"},
|
||||
ExitCode: &ProcessExitCodeVerbose{
|
||||
Status: []string{"EXITED"},
|
||||
ReturnCode: &Uint32NoVal{Set: Ptr(true), Number: Ptr(int64(0))},
|
||||
},
|
||||
Nodes: &StepNodes{
|
||||
Count: Ptr(int32(2)),
|
||||
Range: Ptr("node[01-02]"),
|
||||
List: Hostlist{"node01", "node02"},
|
||||
},
|
||||
Tasks: &StepTasks{Count: Ptr(int32(4))},
|
||||
Time: &StepTime{
|
||||
Elapsed: Ptr(int32(60)),
|
||||
Start: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1234567890))},
|
||||
End: &Uint64NoVal{Set: Ptr(true), Number: Ptr(int64(1234567950))},
|
||||
},
|
||||
Tres: &StepTres{
|
||||
Allocated: TresList{
|
||||
{Type: Ptr("cpu"), Count: Ptr(int64(4))},
|
||||
},
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var decoded Step
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if decoded.Step == nil || decoded.Step.ID == nil || *decoded.Step.ID != "0" {
|
||||
t.Fatal("step.id mismatch")
|
||||
}
|
||||
if decoded.Nodes == nil || decoded.Nodes.Count == nil || *decoded.Nodes.Count != 2 {
|
||||
t.Fatal("nodes.count mismatch")
|
||||
}
|
||||
if len(decoded.Nodes.List) != 2 {
|
||||
t.Fatalf("nodes.list mismatch: %v", decoded.Nodes.List)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user