style(frontend): gofmt the apiserver and xcecli sources
Formatting only (gofmt -w on the 29 non-vendored, non-generated files gofmt -l reported); swag-generated apiserver/docs/docs.go left as is. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b57ce6d6a9
commit
3615c7c0fb
29 changed files with 1280 additions and 1285 deletions
|
|
@ -23,10 +23,10 @@ config_file:
|
|||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
user string
|
||||
pass string
|
||||
wantErr bool
|
||||
name string
|
||||
user string
|
||||
pass string
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid admin", "admin", "secret", false},
|
||||
{"valid viewer", "viewer", "pass", false},
|
||||
|
|
|
|||
|
|
@ -515,4 +515,4 @@ func TestIntegration_CrossFeature_SequentialConfigPushes(t *testing.T) {
|
|||
if hb := atomic.LoadInt32(&heartbeatIntervalSecs); hb != 60 {
|
||||
t.Errorf("heartbeatIntervalSecs = %d, want 60", hb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
_ "apiserver/docs"
|
||||
"apiserver/backend"
|
||||
"apiserver/backend/auth"
|
||||
_ "apiserver/docs"
|
||||
"apiserver/middleware"
|
||||
"apiserver/router"
|
||||
"apiserver/store"
|
||||
|
|
|
|||
|
|
@ -44,16 +44,16 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
SUCCESS_CODE = 200
|
||||
CREATED = 201
|
||||
ACCEPTED = 202
|
||||
StatusPartialContent = 206
|
||||
ERROR_CODE = 500
|
||||
INVALID_PARAMS_CODE = 400
|
||||
ERROR_AUTH_CHECK_TOKEN_CODE = 401
|
||||
ERROR_AUTH_CODE = 403
|
||||
ERROR_NOT_FOUND_CODE = 404
|
||||
ERROR_TOO_MANY_REQUESTS_CODE = 429
|
||||
SUCCESS_CODE = 200
|
||||
CREATED = 201
|
||||
ACCEPTED = 202
|
||||
StatusPartialContent = 206
|
||||
ERROR_CODE = 500
|
||||
INVALID_PARAMS_CODE = 400
|
||||
ERROR_AUTH_CHECK_TOKEN_CODE = 401
|
||||
ERROR_AUTH_CODE = 403
|
||||
ERROR_NOT_FOUND_CODE = 404
|
||||
ERROR_TOO_MANY_REQUESTS_CODE = 429
|
||||
|
||||
//read file error
|
||||
ERROR_READ_FILE_CODE = 501
|
||||
|
|
|
|||
|
|
@ -675,4 +675,4 @@ func TestIntegration_SpecialCharactersInHostname(t *testing.T) {
|
|||
if hostnameVal != xssHostname {
|
||||
t.Errorf("hostname round-trip: got %q, want %q", hostnameVal, xssHostname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
const (
|
||||
//TODO: Need assign clusterID globally.
|
||||
clusterID = 1
|
||||
clusterID = 1
|
||||
apiserverID = 0
|
||||
)
|
||||
|
||||
|
|
@ -3190,4 +3190,3 @@ func GetHostList(hosts []string) ([]string, error) {
|
|||
}
|
||||
return execHosts, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"apiserver/backend"
|
||||
"apiserver/models"
|
||||
"apiserver/store"
|
||||
"apiserver/utils"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
|
|||
|
|
@ -242,4 +242,3 @@ func parseJobIDParam(jobidStr string) (models.JobInfo, error) {
|
|||
ArrayIdx: 0, // peek defaults to base job; array idx can be added later
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ func TestGetJobOutput_DefaultPathFallback(t *testing.T) {
|
|||
// Use a real-ish packed job ID: cluster=1, apiserver=0, ts=1778311965, seq=0
|
||||
// FormatJobID → "1-0-1778311965-0"
|
||||
// WorkerDefaultOutputPath → "/tmp/job_1-0-1778311965-0_task0.out"
|
||||
const uniqueID = int64(0x0100000006A04BDD<<0) // MakeJobID(1,0,1778311965,0,0)
|
||||
const uniqueID = int64(0x0100000006A04BDD << 0) // MakeJobID(1,0,1778311965,0,0)
|
||||
// Use backend.MakeJobID directly for clarity; compute inline:
|
||||
// clusterID=1 at bit 56, timestamp=1778311965 at bit 20
|
||||
// packed = (1 << 56) | (1778311965 << 20)
|
||||
|
|
@ -512,4 +512,3 @@ func TestGetJobOutput_DoneJob(t *testing.T) {
|
|||
t.Errorf("job_status = %q, want DONE", resp.Data.JobStatus)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,46 +7,46 @@ import (
|
|||
|
||||
func TestClusterInfo_NewFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
name string
|
||||
cluster ClusterInfo
|
||||
field string
|
||||
field string
|
||||
want interface{}
|
||||
}{
|
||||
{
|
||||
name: "HeartbeatInterval field exists and serializes",
|
||||
name: "HeartbeatInterval field exists and serializes",
|
||||
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 30},
|
||||
field: "heartbeat_interval",
|
||||
want: float64(30),
|
||||
field: "heartbeat_interval",
|
||||
want: float64(30),
|
||||
},
|
||||
{
|
||||
name: "Vcpus field exists and serializes",
|
||||
name: "Vcpus field exists and serializes",
|
||||
cluster: ClusterInfo{ClusterName: "test", Vcpus: 16},
|
||||
field: "vcpus",
|
||||
want: float64(16),
|
||||
field: "vcpus",
|
||||
want: float64(16),
|
||||
},
|
||||
{
|
||||
name: "InactiveHeartbeatCount field exists and serializes",
|
||||
name: "InactiveHeartbeatCount field exists and serializes",
|
||||
cluster: ClusterInfo{ClusterName: "test", InactiveHeartbeatCount: 3},
|
||||
field: "inactive_heartbeat_count",
|
||||
want: float64(3),
|
||||
field: "inactive_heartbeat_count",
|
||||
want: float64(3),
|
||||
},
|
||||
{
|
||||
name: "HeartbeatInterval zero value serializes as 0 not omitted",
|
||||
name: "HeartbeatInterval zero value serializes as 0 not omitted",
|
||||
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 0, Vcpus: 0, InactiveHeartbeatCount: 0},
|
||||
field: "heartbeat_interval",
|
||||
want: float64(0),
|
||||
field: "heartbeat_interval",
|
||||
want: float64(0),
|
||||
},
|
||||
{
|
||||
name: "Vcpus zero value serializes as 0 not omitted",
|
||||
name: "Vcpus zero value serializes as 0 not omitted",
|
||||
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 0, Vcpus: 0, InactiveHeartbeatCount: 0},
|
||||
field: "vcpus",
|
||||
want: float64(0),
|
||||
field: "vcpus",
|
||||
want: float64(0),
|
||||
},
|
||||
{
|
||||
name: "InactiveHeartbeatCount zero value serializes as 0 not omitted",
|
||||
name: "InactiveHeartbeatCount zero value serializes as 0 not omitted",
|
||||
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 0, Vcpus: 0, InactiveHeartbeatCount: 0},
|
||||
field: "inactive_heartbeat_count",
|
||||
want: float64(0),
|
||||
field: "inactive_heartbeat_count",
|
||||
want: float64(0),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -122,4 +122,4 @@ func mapKeys(m map[string]interface{}) []string {
|
|||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
package models
|
||||
|
||||
const (
|
||||
CONTROL_JOB = 1
|
||||
CONTROL_HOST = 2
|
||||
CONTROL_QUEUE = 3
|
||||
CONTROL_CLUSTER = 4
|
||||
CONTROL_DEBUG = 5
|
||||
)
|
||||
|
||||
type ActionControl struct {
|
||||
TargetType int `json:"target_type"`
|
||||
ActionType int `json:"action_type"`
|
||||
Content Jsonb `json:"content"`
|
||||
SubmitTime int64 `json:"submit_time"`
|
||||
Submitter string `json:"submitter"`
|
||||
}
|
||||
package models
|
||||
|
||||
const (
|
||||
CONTROL_JOB = 1
|
||||
CONTROL_HOST = 2
|
||||
CONTROL_QUEUE = 3
|
||||
CONTROL_CLUSTER = 4
|
||||
CONTROL_DEBUG = 5
|
||||
)
|
||||
|
||||
type ActionControl struct {
|
||||
TargetType int `json:"target_type"`
|
||||
ActionType int `json:"action_type"`
|
||||
Content Jsonb `json:"content"`
|
||||
SubmitTime int64 `json:"submit_time"`
|
||||
Submitter string `json:"submitter"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,9 +88,9 @@ type HostInfo struct {
|
|||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
//NumRsv int `json:"num_rsv"`
|
||||
NumDone int `json:"num_done"`
|
||||
Topology string `json:"topology"` // JSONB
|
||||
SchedStop string `json:"sched_stop"` // JSONB
|
||||
NumDone int `json:"num_done"`
|
||||
Topology string `json:"topology"` // JSONB
|
||||
SchedStop string `json:"sched_stop"` // JSONB
|
||||
ResourceValues []ResourceDistributionInfo `json:"resource_values"` // per-worker resource values
|
||||
//RealLoad string `json:"real_load"` // JSONB
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,4 +68,4 @@ func TestHostStateStr(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -591,32 +591,32 @@ type ArrayJobInfo struct {
|
|||
}
|
||||
|
||||
type JobAdditionalFields struct {
|
||||
JobID int64 `json:"job_id"`
|
||||
SubmitUser string `json:"submit_user"`
|
||||
JobStatus int `json:"job_status"`
|
||||
JobID int64 `json:"job_id"`
|
||||
SubmitUser string `json:"submit_user"`
|
||||
JobStatus int `json:"job_status"`
|
||||
SubmitTime int64 `json:"submit_time"`
|
||||
FromHost string `json:"from_host"`
|
||||
ExecHosts string `json:"exec_hosts"`
|
||||
|
||||
StartTime int64 `json:"start_time"`
|
||||
EndTime int64 `json:"end_time"`
|
||||
JobRusage Jsonb `json:"job_rusage"`
|
||||
MaxMem float64 `json:"max_mem"`
|
||||
AvgMem float64 `json:"avg_mem"`
|
||||
UsedMem float64 `json:"used_mem"`
|
||||
CPUTime int `json:"cpu_time"`
|
||||
PeakCPUTime int `json:"peak_cpu_time"`
|
||||
PeakCPUDuration float64 `json:"peak_cpu_duration"`
|
||||
MemEfficiency float64 `json:"mem_efficiency"` // Memory efficiency in percentage
|
||||
AvgCPUEfficiency float64 `json:"avg_cpu_efficiency"` // Average CPU efficiency in percentage
|
||||
PeakCPUEfficiency float64 `json:"peak_cpu_efficiency"` // Peak CPU efficiency in percentage
|
||||
NumHosts int `json:"num_hosts"` // Number of hosts used by the job
|
||||
FirstHost string `json:"first_host"` // First host where the job ran
|
||||
AllocSlot string `json:"alloc_slots"` // Allocated slot information
|
||||
PIDInfo Jsonb `json:"pid_info"`
|
||||
Pids []int `json:"pids"` // List of process IDs associated with the job
|
||||
Pgids []int `json:"pgids"` // List of process group IDs associated with the job
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
StartTime int64 `json:"start_time"`
|
||||
EndTime int64 `json:"end_time"`
|
||||
JobRusage Jsonb `json:"job_rusage"`
|
||||
MaxMem float64 `json:"max_mem"`
|
||||
AvgMem float64 `json:"avg_mem"`
|
||||
UsedMem float64 `json:"used_mem"`
|
||||
CPUTime int `json:"cpu_time"`
|
||||
PeakCPUTime int `json:"peak_cpu_time"`
|
||||
PeakCPUDuration float64 `json:"peak_cpu_duration"`
|
||||
MemEfficiency float64 `json:"mem_efficiency"` // Memory efficiency in percentage
|
||||
AvgCPUEfficiency float64 `json:"avg_cpu_efficiency"` // Average CPU efficiency in percentage
|
||||
PeakCPUEfficiency float64 `json:"peak_cpu_efficiency"` // Peak CPU efficiency in percentage
|
||||
NumHosts int `json:"num_hosts"` // Number of hosts used by the job
|
||||
FirstHost string `json:"first_host"` // First host where the job ran
|
||||
AllocSlot string `json:"alloc_slots"` // Allocated slot information
|
||||
PIDInfo Jsonb `json:"pid_info"`
|
||||
Pids []int `json:"pids"` // List of process IDs associated with the job
|
||||
Pgids []int `json:"pgids"` // List of process group IDs associated with the job
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
// CoreLimit/MemLimit: JobInfo has no equivalent field (these are
|
||||
// submission-time resource limits, not job-state fields), but xcecli's
|
||||
// JobFieldsMap accepts them as valid -o/-l display fields (bug-1674) --
|
||||
|
|
|
|||
|
|
@ -1,139 +1,139 @@
|
|||
package models
|
||||
|
||||
type ValueType int
|
||||
|
||||
const (
|
||||
STRING ValueType = iota // Bool value
|
||||
NUMERIC // Numeric value
|
||||
BOOLEAN // String value
|
||||
)
|
||||
|
||||
// String returns the string representation of ValueType
|
||||
func (v ValueType) String() string {
|
||||
switch v {
|
||||
case BOOLEAN:
|
||||
return "Boolean"
|
||||
case NUMERIC:
|
||||
return "Numeric"
|
||||
case STRING:
|
||||
return "String"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type OrderType int
|
||||
|
||||
const (
|
||||
INCR OrderType = iota
|
||||
DECR
|
||||
NA
|
||||
)
|
||||
|
||||
// String returns the string representation of OrderType
|
||||
func (o OrderType) String() string {
|
||||
switch o {
|
||||
case INCR:
|
||||
return "Incr"
|
||||
case DECR:
|
||||
return "Decr"
|
||||
case NA:
|
||||
return "NA"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
var RESOURCE_DEFAULT_COLUMN = []string{
|
||||
"res_name",
|
||||
"description",
|
||||
"value_type",
|
||||
"order_type",
|
||||
"flag",
|
||||
"interval",
|
||||
}
|
||||
|
||||
var RESOURCE_DISTRIBUTION_DEFAULT_COLUMN = []string{
|
||||
"res_name",
|
||||
"numeric_value",
|
||||
"location",
|
||||
"is_dynamic",
|
||||
}
|
||||
|
||||
var RESOURCE_DEFINITION_DEFAULT_COLUMN = []string{
|
||||
"res_name",
|
||||
"description",
|
||||
"value_type",
|
||||
"order_type",
|
||||
"flag",
|
||||
"interval",
|
||||
}
|
||||
|
||||
var ResourceFieldMappings = map[string]string{
|
||||
// API field name → struct field name
|
||||
"value": "numeric_value",
|
||||
}
|
||||
|
||||
var ResourcesAvailableTypes = []string{"static", "dynamic"}
|
||||
|
||||
const (
|
||||
BUILTIN = 0x01
|
||||
DYNAMIC = 0x02
|
||||
RELEASE = 0x20
|
||||
CONSUMABLE = 0x80
|
||||
)
|
||||
|
||||
type FlagValue int
|
||||
|
||||
// ToJSON returns the JSON object representation of FlagValue
|
||||
func (f FlagValue) ToJSON() map[string]bool {
|
||||
return map[string]bool{
|
||||
"builtin": f&BUILTIN != 0,
|
||||
"dynamic": f&DYNAMIC != 0,
|
||||
"release": f&RELEASE != 0,
|
||||
"consumable": f&CONSUMABLE != 0,
|
||||
}
|
||||
}
|
||||
|
||||
type ResourceInfo struct {
|
||||
ResName string
|
||||
Description string
|
||||
ValueType ValueType
|
||||
OrderType OrderType
|
||||
Flag int
|
||||
Interval int
|
||||
}
|
||||
|
||||
// queues info query supported filter
|
||||
var RESOURCE_DISTRIBUTION_SUPPORT_FILTERS = []string{
|
||||
"res_name",
|
||||
"is_dynamic",
|
||||
"location",
|
||||
}
|
||||
|
||||
var RESOURCE_DEFINITION_SUPPORT_FILTERS = []string{
|
||||
"res_name",
|
||||
}
|
||||
|
||||
type ResourceDistributionInfo struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
IsDynamic bool `json:"is_dynamic"`
|
||||
ValueType int `json:"value_type"`
|
||||
BooleanValue bool `json:"boolean_value"`
|
||||
StringValue string `json:"string_value"`
|
||||
NumericValue float64 `json:"numeric_value"`
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
type ResourceDefinitionInfo struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
Description string `json:"description"`
|
||||
ValueType int `json:"value_type"`
|
||||
OrderType int `json:"order_type"`
|
||||
Flag int `json:"flag"`
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
|
||||
var RESOURCE_DISTRIBUTION_SUPPORT_SORT = []string{
|
||||
"res_name",
|
||||
}
|
||||
package models
|
||||
|
||||
type ValueType int
|
||||
|
||||
const (
|
||||
STRING ValueType = iota // Bool value
|
||||
NUMERIC // Numeric value
|
||||
BOOLEAN // String value
|
||||
)
|
||||
|
||||
// String returns the string representation of ValueType
|
||||
func (v ValueType) String() string {
|
||||
switch v {
|
||||
case BOOLEAN:
|
||||
return "Boolean"
|
||||
case NUMERIC:
|
||||
return "Numeric"
|
||||
case STRING:
|
||||
return "String"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type OrderType int
|
||||
|
||||
const (
|
||||
INCR OrderType = iota
|
||||
DECR
|
||||
NA
|
||||
)
|
||||
|
||||
// String returns the string representation of OrderType
|
||||
func (o OrderType) String() string {
|
||||
switch o {
|
||||
case INCR:
|
||||
return "Incr"
|
||||
case DECR:
|
||||
return "Decr"
|
||||
case NA:
|
||||
return "NA"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
var RESOURCE_DEFAULT_COLUMN = []string{
|
||||
"res_name",
|
||||
"description",
|
||||
"value_type",
|
||||
"order_type",
|
||||
"flag",
|
||||
"interval",
|
||||
}
|
||||
|
||||
var RESOURCE_DISTRIBUTION_DEFAULT_COLUMN = []string{
|
||||
"res_name",
|
||||
"numeric_value",
|
||||
"location",
|
||||
"is_dynamic",
|
||||
}
|
||||
|
||||
var RESOURCE_DEFINITION_DEFAULT_COLUMN = []string{
|
||||
"res_name",
|
||||
"description",
|
||||
"value_type",
|
||||
"order_type",
|
||||
"flag",
|
||||
"interval",
|
||||
}
|
||||
|
||||
var ResourceFieldMappings = map[string]string{
|
||||
// API field name → struct field name
|
||||
"value": "numeric_value",
|
||||
}
|
||||
|
||||
var ResourcesAvailableTypes = []string{"static", "dynamic"}
|
||||
|
||||
const (
|
||||
BUILTIN = 0x01
|
||||
DYNAMIC = 0x02
|
||||
RELEASE = 0x20
|
||||
CONSUMABLE = 0x80
|
||||
)
|
||||
|
||||
type FlagValue int
|
||||
|
||||
// ToJSON returns the JSON object representation of FlagValue
|
||||
func (f FlagValue) ToJSON() map[string]bool {
|
||||
return map[string]bool{
|
||||
"builtin": f&BUILTIN != 0,
|
||||
"dynamic": f&DYNAMIC != 0,
|
||||
"release": f&RELEASE != 0,
|
||||
"consumable": f&CONSUMABLE != 0,
|
||||
}
|
||||
}
|
||||
|
||||
type ResourceInfo struct {
|
||||
ResName string
|
||||
Description string
|
||||
ValueType ValueType
|
||||
OrderType OrderType
|
||||
Flag int
|
||||
Interval int
|
||||
}
|
||||
|
||||
// queues info query supported filter
|
||||
var RESOURCE_DISTRIBUTION_SUPPORT_FILTERS = []string{
|
||||
"res_name",
|
||||
"is_dynamic",
|
||||
"location",
|
||||
}
|
||||
|
||||
var RESOURCE_DEFINITION_SUPPORT_FILTERS = []string{
|
||||
"res_name",
|
||||
}
|
||||
|
||||
type ResourceDistributionInfo struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
IsDynamic bool `json:"is_dynamic"`
|
||||
ValueType int `json:"value_type"`
|
||||
BooleanValue bool `json:"boolean_value"`
|
||||
StringValue string `json:"string_value"`
|
||||
NumericValue float64 `json:"numeric_value"`
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
type ResourceDefinitionInfo struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
Description string `json:"description"`
|
||||
ValueType int `json:"value_type"`
|
||||
OrderType int `json:"order_type"`
|
||||
Flag int `json:"flag"`
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
|
||||
var RESOURCE_DISTRIBUTION_SUPPORT_SORT = []string{
|
||||
"res_name",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ func TestIntegration_ConcurrentResourceValuePushes(t *testing.T) {
|
|||
ResourceName: "mem",
|
||||
Location: "node01",
|
||||
NumericValue: float64(idx + 1),
|
||||
ValueType: 1, // NUMERIC
|
||||
ValueType: 1, // NUMERIC
|
||||
IsDynamic: true,
|
||||
})
|
||||
count.Add(1)
|
||||
|
|
@ -129,4 +129,4 @@ func TestIntegration_ConcurrentResourceValuePushes(t *testing.T) {
|
|||
if ResourceLocations.Count() != 1 {
|
||||
t.Errorf("ResourceLocations count = %d, want 1 (key exists exactly once)", ResourceLocations.Count())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@ import "apiserver/models"
|
|||
|
||||
// Global store instances.
|
||||
var (
|
||||
Jobs *DataStore[uint64, models.JobInfo]
|
||||
ArrayJobs *DataStore[uint64, models.ArrayJobInfo]
|
||||
Queues *DataStore[string, models.QueueInfo]
|
||||
Hosts *DataStore[string, models.HostInfo]
|
||||
Jobs *DataStore[uint64, models.JobInfo]
|
||||
ArrayJobs *DataStore[uint64, models.ArrayJobInfo]
|
||||
Queues *DataStore[string, models.QueueInfo]
|
||||
Hosts *DataStore[string, models.HostInfo]
|
||||
Resources *DataStore[string, models.ResourceDefinitionInfo]
|
||||
ResourceLocations *DataStore[string, models.ResourceDistributionInfo]
|
||||
Clusters *DataStore[string, models.ClusterInfo]
|
||||
JobExecHosts *DataStore[int64, models.JobExecHosts]
|
||||
Roles *DataStore[string, RoleEntry]
|
||||
UserRoles *DataStore[string, []string] // user_name → role_names
|
||||
JobExecHosts *DataStore[int64, models.JobExecHosts]
|
||||
Roles *DataStore[string, RoleEntry]
|
||||
UserRoles *DataStore[string, []string] // user_name → role_names
|
||||
)
|
||||
|
||||
// RoleEntry holds a role's permissions.
|
||||
|
|
|
|||
|
|
@ -1,50 +1,50 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
"xcecli/common/request"
|
||||
"xcecli/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewListCmd create the list command
|
||||
func NewListCmd(configManager *config.ConfigManager) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
GroupID: "api",
|
||||
Short: "List the API Servers",
|
||||
Long: "List all the configured API Servers and the default API Server",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(configManager)
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runList(cm *config.ConfigManager) error {
|
||||
cfg, err := cm.GetConfig()
|
||||
if err != nil {
|
||||
errMsg := request.ExtractErrorMessage(err)
|
||||
return fmt.Errorf("failed to get the config: [%s]", errMsg)
|
||||
}
|
||||
|
||||
// use tabwriter to format the output
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "Default\tVersion\tURL")
|
||||
|
||||
for _, server := range cfg.APIServerInfo {
|
||||
isDefault := " "
|
||||
if server.Token == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\n",
|
||||
isDefault,
|
||||
server.URL)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
"xcecli/common/request"
|
||||
"xcecli/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewListCmd create the list command
|
||||
func NewListCmd(configManager *config.ConfigManager) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
GroupID: "api",
|
||||
Short: "List the API Servers",
|
||||
Long: "List all the configured API Servers and the default API Server",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(configManager)
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runList(cm *config.ConfigManager) error {
|
||||
cfg, err := cm.GetConfig()
|
||||
if err != nil {
|
||||
errMsg := request.ExtractErrorMessage(err)
|
||||
return fmt.Errorf("failed to get the config: [%s]", errMsg)
|
||||
}
|
||||
|
||||
// use tabwriter to format the output
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "Default\tVersion\tURL")
|
||||
|
||||
for _, server := range cfg.APIServerInfo {
|
||||
isDefault := " "
|
||||
if server.Token == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\n",
|
||||
isDefault,
|
||||
server.URL)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +1,38 @@
|
|||
package jobs
|
||||
|
||||
import (
|
||||
"xcecli/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewJobsCmd creates a jobs query command
|
||||
func NewJobsCmd(configManager *config.ConfigManager) *cobra.Command {
|
||||
|
||||
// Create the parent jobs command
|
||||
cmd := &cobra.Command{
|
||||
Use: "jobs",
|
||||
GroupID: "query",
|
||||
Short: "Manage jobs",
|
||||
Long: `Manage jobs in the system.
|
||||
Use 'xce jobs list' to query jobs information.
|
||||
Use 'xce jobs <action>' to submit/kill/stop/resume/checkpoint/restart jobs.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Display help when no subcommand is specified
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(NewListJobsCmd(configManager))
|
||||
// Add submit command to jobs command
|
||||
cmd.AddCommand(NewSubmitJobsCmd(configManager))
|
||||
// Add job control sub-commands to jobs command
|
||||
cmd.AddCommand(NewKillJobsCmd(configManager))
|
||||
cmd.AddCommand(NewStopJobsCmd(configManager))
|
||||
cmd.AddCommand(NewResumeJobsCmd(configManager))
|
||||
cmd.AddCommand(NewCheckPointJobsCmd(configManager))
|
||||
cmd.AddCommand(NewRestartJobsCmd(configManager))
|
||||
// Add peek command to view job output
|
||||
cmd.AddCommand(NewPeekJobsCmd(configManager))
|
||||
|
||||
return cmd
|
||||
}
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"xcecli/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewJobsCmd creates a jobs query command
|
||||
func NewJobsCmd(configManager *config.ConfigManager) *cobra.Command {
|
||||
|
||||
// Create the parent jobs command
|
||||
cmd := &cobra.Command{
|
||||
Use: "jobs",
|
||||
GroupID: "query",
|
||||
Short: "Manage jobs",
|
||||
Long: `Manage jobs in the system.
|
||||
Use 'xce jobs list' to query jobs information.
|
||||
Use 'xce jobs <action>' to submit/kill/stop/resume/checkpoint/restart jobs.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Display help when no subcommand is specified
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(NewListJobsCmd(configManager))
|
||||
// Add submit command to jobs command
|
||||
cmd.AddCommand(NewSubmitJobsCmd(configManager))
|
||||
// Add job control sub-commands to jobs command
|
||||
cmd.AddCommand(NewKillJobsCmd(configManager))
|
||||
cmd.AddCommand(NewStopJobsCmd(configManager))
|
||||
cmd.AddCommand(NewResumeJobsCmd(configManager))
|
||||
cmd.AddCommand(NewCheckPointJobsCmd(configManager))
|
||||
cmd.AddCommand(NewRestartJobsCmd(configManager))
|
||||
// Add peek command to view job output
|
||||
cmd.AddCommand(NewPeekJobsCmd(configManager))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,111 +1,111 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2025 xce
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"xcecli/config"
|
||||
logger "xcecli/config"
|
||||
cmd "xcecli/router"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// GitCommit and BuildTime are injected at link time via
|
||||
// `-ldflags "-X main.GitCommit=... -X main.BuildTime=..."` (see
|
||||
// src/tools/go_build). The
|
||||
// "unknown" defaults matter: a bare `go build`/`go run` that bypasses that
|
||||
// ldflags injection must still report "unknown" rather than an empty
|
||||
// string, matching this feature's unknown-fallback requirement (see
|
||||
// xce_version_field() in src/lib/utils.c for the equivalent C-side
|
||||
// convention).
|
||||
//
|
||||
// DirtyFingerprint is injected the same way (`-X main.DirtyFingerprint=...`,
|
||||
// see src/tools/go_build task 4.1) but its default is a deliberately EMPTY
|
||||
// string, not "unknown" -- matching dirty_fingerprint's convention
|
||||
// everywhere else in the dirty-content-fingerprint feature (empty means
|
||||
// "no fingerprint available", distinct from GitCommit/BuildTime's
|
||||
// "unknown" sentinel for "not injected at all").
|
||||
var (
|
||||
GitCommit = "unknown"
|
||||
BuildTime = "unknown"
|
||||
DirtyFingerprint = ""
|
||||
)
|
||||
|
||||
// composeVersionDisplay applies the dirty-content-fingerprint feature's
|
||||
// strip-and-append rule to gitCommit: if dirtyFingerprint is non-empty, a
|
||||
// trailing "-dirty" suffix on gitCommit is stripped (an anchored suffix
|
||||
// check via strings.TrimSuffix, not a blind substring replace) and
|
||||
// "-<dirtyFingerprint>" is appended in its place; otherwise gitCommit is
|
||||
// returned exactly as-is (already correctly showing "-dirty" or not, per
|
||||
// the original build-time-versioning feature). Mirrors
|
||||
// xce_compose_version_ex() in src/lib/utils.c (task 3.1) and apiserver's
|
||||
// own composeVersionDisplay (task 4.4), the equivalent implementations
|
||||
// already approved for the other version-displaying components.
|
||||
//
|
||||
// xcecli has no startup log to protect (already established as out of
|
||||
// scope by the original build-time-versioning feature, see that feature's
|
||||
// own task 4.5 commit fb09afe), so this result feeds the ONLY consumption
|
||||
// point below: the version string passed into router.Execute(cm, version),
|
||||
// which sets rootCmd.Version for Cobra's native --version/-v handling.
|
||||
func composeVersionDisplay(gitCommit, dirtyFingerprint string) string {
|
||||
if dirtyFingerprint == "" {
|
||||
return gitCommit
|
||||
}
|
||||
return strings.TrimSuffix(gitCommit, "-dirty") + "-" + dirtyFingerprint
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// init logger
|
||||
if err := logger.InitLogger(); err != nil {
|
||||
fmt.Println(strings.ToLower(err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.Info("********** Initialize Logger on client side done!")
|
||||
|
||||
// initialize the config manager
|
||||
configManager, err := config.NewConfigManager()
|
||||
if err != nil {
|
||||
fmt.Println("failed to initialize the config manager", zap.Error(err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// ensure the config manager is initialized correctly
|
||||
if configManager == nil {
|
||||
fmt.Println("failed to initialize the config manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// execute the command
|
||||
version := fmt.Sprintf("%s built %s", composeVersionDisplay(GitCommit, DirtyFingerprint), BuildTime)
|
||||
if err := cmd.Execute(configManager, version); err != nil {
|
||||
fmt.Println(strings.ToLower(err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2025 xce
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"xcecli/config"
|
||||
logger "xcecli/config"
|
||||
cmd "xcecli/router"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// GitCommit and BuildTime are injected at link time via
|
||||
// `-ldflags "-X main.GitCommit=... -X main.BuildTime=..."` (see
|
||||
// src/tools/go_build). The
|
||||
// "unknown" defaults matter: a bare `go build`/`go run` that bypasses that
|
||||
// ldflags injection must still report "unknown" rather than an empty
|
||||
// string, matching this feature's unknown-fallback requirement (see
|
||||
// xce_version_field() in src/lib/utils.c for the equivalent C-side
|
||||
// convention).
|
||||
//
|
||||
// DirtyFingerprint is injected the same way (`-X main.DirtyFingerprint=...`,
|
||||
// see src/tools/go_build task 4.1) but its default is a deliberately EMPTY
|
||||
// string, not "unknown" -- matching dirty_fingerprint's convention
|
||||
// everywhere else in the dirty-content-fingerprint feature (empty means
|
||||
// "no fingerprint available", distinct from GitCommit/BuildTime's
|
||||
// "unknown" sentinel for "not injected at all").
|
||||
var (
|
||||
GitCommit = "unknown"
|
||||
BuildTime = "unknown"
|
||||
DirtyFingerprint = ""
|
||||
)
|
||||
|
||||
// composeVersionDisplay applies the dirty-content-fingerprint feature's
|
||||
// strip-and-append rule to gitCommit: if dirtyFingerprint is non-empty, a
|
||||
// trailing "-dirty" suffix on gitCommit is stripped (an anchored suffix
|
||||
// check via strings.TrimSuffix, not a blind substring replace) and
|
||||
// "-<dirtyFingerprint>" is appended in its place; otherwise gitCommit is
|
||||
// returned exactly as-is (already correctly showing "-dirty" or not, per
|
||||
// the original build-time-versioning feature). Mirrors
|
||||
// xce_compose_version_ex() in src/lib/utils.c (task 3.1) and apiserver's
|
||||
// own composeVersionDisplay (task 4.4), the equivalent implementations
|
||||
// already approved for the other version-displaying components.
|
||||
//
|
||||
// xcecli has no startup log to protect (already established as out of
|
||||
// scope by the original build-time-versioning feature, see that feature's
|
||||
// own task 4.5 commit fb09afe), so this result feeds the ONLY consumption
|
||||
// point below: the version string passed into router.Execute(cm, version),
|
||||
// which sets rootCmd.Version for Cobra's native --version/-v handling.
|
||||
func composeVersionDisplay(gitCommit, dirtyFingerprint string) string {
|
||||
if dirtyFingerprint == "" {
|
||||
return gitCommit
|
||||
}
|
||||
return strings.TrimSuffix(gitCommit, "-dirty") + "-" + dirtyFingerprint
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// init logger
|
||||
if err := logger.InitLogger(); err != nil {
|
||||
fmt.Println(strings.ToLower(err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.Info("********** Initialize Logger on client side done!")
|
||||
|
||||
// initialize the config manager
|
||||
configManager, err := config.NewConfigManager()
|
||||
if err != nil {
|
||||
fmt.Println("failed to initialize the config manager", zap.Error(err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// ensure the config manager is initialized correctly
|
||||
if configManager == nil {
|
||||
fmt.Println("failed to initialize the config manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// execute the command
|
||||
version := fmt.Sprintf("%s built %s", composeVersionDisplay(GitCommit, DirtyFingerprint), BuildTime)
|
||||
if err := cmd.Execute(configManager, version); err != nil {
|
||||
fmt.Println(strings.ToLower(err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Use 'xce resources definition list' to query resources definition information.`,
|
|||
distributionCmd := &cobra.Command{
|
||||
Use: "distribution",
|
||||
Short: "Manage resource distribution",
|
||||
Long: `Manage resource distribution information.
|
||||
Long: `Manage resource distribution information.
|
||||
Use 'xce resources distribution list' to query resources distribution information.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Display help when no subcommand is specified
|
||||
|
|
@ -39,7 +39,7 @@ Use 'xce resources distribution list' to query resources distribution informatio
|
|||
definitionCmd := &cobra.Command{
|
||||
Use: "definition",
|
||||
Short: "Manage resource definition",
|
||||
Long: `Manage resource definition information.
|
||||
Long: `Manage resource definition information.
|
||||
Use 'xce resources definition list' to query resources definition information.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Display help when no subcommand is specified
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
"xcecli/models"
|
||||
)
|
||||
|
||||
type Output struct {
|
||||
fields string
|
||||
formatJson bool
|
||||
noheader bool
|
||||
verbose bool
|
||||
wideFormat bool
|
||||
jobStatusFilter []string
|
||||
}
|
||||
|
||||
func NewOutput(fields string, formatJson bool, noheader bool) *Output {
|
||||
return &Output{fields: fields, formatJson: formatJson, noheader: noheader}
|
||||
}
|
||||
|
||||
func (o *Output) SetVerbose(verbose bool) {
|
||||
o.verbose = verbose
|
||||
}
|
||||
|
||||
func (o *Output) SetNoHeader(noheader bool) {
|
||||
o.noheader = noheader
|
||||
}
|
||||
|
||||
func (o *Output) SetWideFormat(wideFormat bool) {
|
||||
o.wideFormat = wideFormat
|
||||
}
|
||||
|
||||
func (o *Output) ClusterPrintData(cluster *models.ClusterInfo) {
|
||||
printClustersVerboseOutput(cluster)
|
||||
}
|
||||
|
||||
func printClustersVerboseOutput(clusters *models.ClusterInfo) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||
defer w.Flush()
|
||||
fmt.Fprintf(w, "XCE Standard Cluster\t%s\t%s\n", clusters.Version, "Jul 08 2025")
|
||||
fmt.Fprintf(w, "My cluster name: <%s>\n", clusters.ClusterName)
|
||||
fmt.Fprintf(w, "The cluster status: <%s>\n", clusters.Status)
|
||||
}
|
||||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
"xcecli/models"
|
||||
)
|
||||
|
||||
type Output struct {
|
||||
fields string
|
||||
formatJson bool
|
||||
noheader bool
|
||||
verbose bool
|
||||
wideFormat bool
|
||||
jobStatusFilter []string
|
||||
}
|
||||
|
||||
func NewOutput(fields string, formatJson bool, noheader bool) *Output {
|
||||
return &Output{fields: fields, formatJson: formatJson, noheader: noheader}
|
||||
}
|
||||
|
||||
func (o *Output) SetVerbose(verbose bool) {
|
||||
o.verbose = verbose
|
||||
}
|
||||
|
||||
func (o *Output) SetNoHeader(noheader bool) {
|
||||
o.noheader = noheader
|
||||
}
|
||||
|
||||
func (o *Output) SetWideFormat(wideFormat bool) {
|
||||
o.wideFormat = wideFormat
|
||||
}
|
||||
|
||||
func (o *Output) ClusterPrintData(cluster *models.ClusterInfo) {
|
||||
printClustersVerboseOutput(cluster)
|
||||
}
|
||||
|
||||
func printClustersVerboseOutput(clusters *models.ClusterInfo) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||
defer w.Flush()
|
||||
fmt.Fprintf(w, "XCE Standard Cluster\t%s\t%s\n", clusters.Version, "Jul 08 2025")
|
||||
fmt.Fprintf(w, "My cluster name: <%s>\n", clusters.ClusterName)
|
||||
fmt.Fprintf(w, "The cluster status: <%s>\n", clusters.Status)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,189 +1,189 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
Log *zap.Logger
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLoggerConfigFile = "logger.yaml"
|
||||
defaultLoggerOutputFile = "xcecli_out.log"
|
||||
)
|
||||
|
||||
type LoggerConfig struct {
|
||||
Logging struct {
|
||||
Level string `yaml:"level"`
|
||||
Encoding string `yaml:"encoding"`
|
||||
OutputPath string `yaml:"outputPath"`
|
||||
File struct {
|
||||
MaxSize int `yaml:"maxSize"`
|
||||
MaxBackups int `yaml:"maxBackups"`
|
||||
MaxAge int `yaml:"maxAge"`
|
||||
Compress bool `yaml:"compress"`
|
||||
} `yaml:"file"`
|
||||
EncoderConfig struct {
|
||||
MessageKey string `yaml:"messageKey"`
|
||||
LevelKey string `yaml:"levelKey"`
|
||||
TimeKey string `yaml:"timeKey"`
|
||||
NameKey string `yaml:"nameKey"`
|
||||
CallerKey string `yaml:"callerKey"`
|
||||
FunctionKey string `yaml:"functionKey"`
|
||||
StacktraceKey string `yaml:"stacktraceKey"`
|
||||
EncodeTime string `yaml:"timeEncoder"`
|
||||
EncodeLevel string `yaml:"levelEncoder"`
|
||||
} `yaml:"encoderConfig"`
|
||||
} `yaml:"logging"`
|
||||
}
|
||||
|
||||
func InitLogger() error {
|
||||
var err error
|
||||
err = loadLoggerConfig()
|
||||
return err
|
||||
}
|
||||
|
||||
func loadLoggerConfig() error {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get the user info: [%v]", err)
|
||||
}
|
||||
// read logger configuration file
|
||||
configPath := filepath.Join(usr.HomeDir, ".xce")
|
||||
|
||||
var loggerConfig LoggerConfig
|
||||
v := viper.New()
|
||||
|
||||
v.SetDefault("logging.level", "info") // debug, info, warn, error, fatal
|
||||
v.SetDefault("logging.encoding", "console") // console, json
|
||||
v.SetDefault("logging.outputPath", defaultLoggerOutputFile) // xcecli_out.log
|
||||
v.SetDefault("logging.file.maxSize", 100) // 100MB
|
||||
v.SetDefault("logging.file.maxBackups", 10) // 10
|
||||
v.SetDefault("logging.file.maxAge", 14) // 14 days
|
||||
v.SetDefault("logging.file.compress", true) // true
|
||||
v.SetDefault("logging.encoderConfig.messageKey", "message") // message
|
||||
v.SetDefault("logging.encoderConfig.levelKey", "level") // level
|
||||
v.SetDefault("logging.encoderConfig.timeKey", "timestamp") // timestamp
|
||||
v.SetDefault("logging.encoderConfig.nameKey", "logger") // logger
|
||||
v.SetDefault("logging.encoderConfig.callerKey", "caller") // caller
|
||||
v.SetDefault("logging.encoderConfig.functionKey", "func") // func
|
||||
v.SetDefault("logging.encoderConfig.stacktraceKey", "stacktrace") // stacktrace
|
||||
v.SetDefault("logging.encoderConfig.timeEncoder", "ISO8601") // ISO8601
|
||||
v.SetDefault("logging.encoderConfig.levelEncoder", "capital") // capital
|
||||
v.SetConfigName(defaultLoggerConfigFile)
|
||||
v.SetConfigType("yaml")
|
||||
v.AddConfigPath(configPath)
|
||||
// yamlContent := `# log level: debug, info, warn, error, fatal
|
||||
// # debug: detailed log
|
||||
// # info: normal log
|
||||
// # warn: warning log
|
||||
// # error: error log
|
||||
// # fatal: fatal log` + v.GetString("logging.level")
|
||||
|
||||
err = v.ReadInConfig()
|
||||
if err != nil {
|
||||
os.MkdirAll(configPath, 0755)
|
||||
|
||||
if err = v.WriteConfigAs(filepath.Join(configPath, "logger.yaml")); err != nil {
|
||||
fmt.Printf("failed to write default logger configuration file: [%v]", err)
|
||||
}
|
||||
}
|
||||
err = v.Unmarshal(&loggerConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode into struct: [%v]", err)
|
||||
}
|
||||
// Handle both formats for OutputPath
|
||||
if !filepath.IsAbs(loggerConfig.Logging.OutputPath) {
|
||||
if loggerConfig.Logging.OutputPath == "" {
|
||||
loggerConfig.Logging.OutputPath = defaultLoggerOutputFile
|
||||
}
|
||||
// If OutputPath is relative, prepend the logs directory
|
||||
loggerConfig.Logging.OutputPath = filepath.Join(configPath, "logs", loggerConfig.Logging.OutputPath)
|
||||
}
|
||||
logDir := filepath.Dir(loggerConfig.Logging.OutputPath) // Extract the directory
|
||||
// Create the directory if it does not exist
|
||||
if err = os.MkdirAll(logDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create client logs directory: %w", err)
|
||||
}
|
||||
Log, err = createLogger(loggerConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create client logger: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLogger(loggerConfig LoggerConfig) (*zap.Logger, error) {
|
||||
configLevel, err := zapcore.ParseLevel(loggerConfig.Logging.Level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create log file and set the permission
|
||||
file, err := os.OpenFile(loggerConfig.Logging.OutputPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file.Close()
|
||||
|
||||
// configure lumberjack for log rotation
|
||||
logWriter := &lumberjack.Logger{
|
||||
Filename: loggerConfig.Logging.OutputPath,
|
||||
MaxSize: loggerConfig.Logging.File.MaxSize,
|
||||
MaxBackups: loggerConfig.Logging.File.MaxBackups,
|
||||
MaxAge: loggerConfig.Logging.File.MaxAge,
|
||||
Compress: loggerConfig.Logging.File.Compress,
|
||||
}
|
||||
|
||||
// Create cores for different log levels
|
||||
core := zapcore.NewCore(
|
||||
zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
|
||||
MessageKey: loggerConfig.Logging.EncoderConfig.MessageKey,
|
||||
LevelKey: loggerConfig.Logging.EncoderConfig.LevelKey,
|
||||
TimeKey: loggerConfig.Logging.EncoderConfig.TimeKey,
|
||||
NameKey: loggerConfig.Logging.EncoderConfig.NameKey,
|
||||
CallerKey: loggerConfig.Logging.EncoderConfig.CallerKey,
|
||||
StacktraceKey: "", // disable stacktrace
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
ConsoleSeparator: " ",
|
||||
}),
|
||||
zapcore.AddSync(logWriter),
|
||||
configLevel,
|
||||
)
|
||||
logger := zap.New(core,
|
||||
zap.AddCaller(), // caller info
|
||||
zap.AddStacktrace(zap.ErrorLevel), // show error stacktrace
|
||||
zap.AddCallerSkip(1), // skip one layer of caller
|
||||
)
|
||||
return logger, nil
|
||||
|
||||
}
|
||||
|
||||
func Info(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Info(msg, fields...)
|
||||
}
|
||||
func Error(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Error(msg, fields...)
|
||||
}
|
||||
|
||||
func Warn(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Warn(msg, fields...)
|
||||
}
|
||||
|
||||
func Debug(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(true)).Debug(msg, fields...)
|
||||
}
|
||||
|
||||
func Fatal(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Fatal(msg, fields...)
|
||||
}
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
Log *zap.Logger
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLoggerConfigFile = "logger.yaml"
|
||||
defaultLoggerOutputFile = "xcecli_out.log"
|
||||
)
|
||||
|
||||
type LoggerConfig struct {
|
||||
Logging struct {
|
||||
Level string `yaml:"level"`
|
||||
Encoding string `yaml:"encoding"`
|
||||
OutputPath string `yaml:"outputPath"`
|
||||
File struct {
|
||||
MaxSize int `yaml:"maxSize"`
|
||||
MaxBackups int `yaml:"maxBackups"`
|
||||
MaxAge int `yaml:"maxAge"`
|
||||
Compress bool `yaml:"compress"`
|
||||
} `yaml:"file"`
|
||||
EncoderConfig struct {
|
||||
MessageKey string `yaml:"messageKey"`
|
||||
LevelKey string `yaml:"levelKey"`
|
||||
TimeKey string `yaml:"timeKey"`
|
||||
NameKey string `yaml:"nameKey"`
|
||||
CallerKey string `yaml:"callerKey"`
|
||||
FunctionKey string `yaml:"functionKey"`
|
||||
StacktraceKey string `yaml:"stacktraceKey"`
|
||||
EncodeTime string `yaml:"timeEncoder"`
|
||||
EncodeLevel string `yaml:"levelEncoder"`
|
||||
} `yaml:"encoderConfig"`
|
||||
} `yaml:"logging"`
|
||||
}
|
||||
|
||||
func InitLogger() error {
|
||||
var err error
|
||||
err = loadLoggerConfig()
|
||||
return err
|
||||
}
|
||||
|
||||
func loadLoggerConfig() error {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get the user info: [%v]", err)
|
||||
}
|
||||
// read logger configuration file
|
||||
configPath := filepath.Join(usr.HomeDir, ".xce")
|
||||
|
||||
var loggerConfig LoggerConfig
|
||||
v := viper.New()
|
||||
|
||||
v.SetDefault("logging.level", "info") // debug, info, warn, error, fatal
|
||||
v.SetDefault("logging.encoding", "console") // console, json
|
||||
v.SetDefault("logging.outputPath", defaultLoggerOutputFile) // xcecli_out.log
|
||||
v.SetDefault("logging.file.maxSize", 100) // 100MB
|
||||
v.SetDefault("logging.file.maxBackups", 10) // 10
|
||||
v.SetDefault("logging.file.maxAge", 14) // 14 days
|
||||
v.SetDefault("logging.file.compress", true) // true
|
||||
v.SetDefault("logging.encoderConfig.messageKey", "message") // message
|
||||
v.SetDefault("logging.encoderConfig.levelKey", "level") // level
|
||||
v.SetDefault("logging.encoderConfig.timeKey", "timestamp") // timestamp
|
||||
v.SetDefault("logging.encoderConfig.nameKey", "logger") // logger
|
||||
v.SetDefault("logging.encoderConfig.callerKey", "caller") // caller
|
||||
v.SetDefault("logging.encoderConfig.functionKey", "func") // func
|
||||
v.SetDefault("logging.encoderConfig.stacktraceKey", "stacktrace") // stacktrace
|
||||
v.SetDefault("logging.encoderConfig.timeEncoder", "ISO8601") // ISO8601
|
||||
v.SetDefault("logging.encoderConfig.levelEncoder", "capital") // capital
|
||||
v.SetConfigName(defaultLoggerConfigFile)
|
||||
v.SetConfigType("yaml")
|
||||
v.AddConfigPath(configPath)
|
||||
// yamlContent := `# log level: debug, info, warn, error, fatal
|
||||
// # debug: detailed log
|
||||
// # info: normal log
|
||||
// # warn: warning log
|
||||
// # error: error log
|
||||
// # fatal: fatal log` + v.GetString("logging.level")
|
||||
|
||||
err = v.ReadInConfig()
|
||||
if err != nil {
|
||||
os.MkdirAll(configPath, 0755)
|
||||
|
||||
if err = v.WriteConfigAs(filepath.Join(configPath, "logger.yaml")); err != nil {
|
||||
fmt.Printf("failed to write default logger configuration file: [%v]", err)
|
||||
}
|
||||
}
|
||||
err = v.Unmarshal(&loggerConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode into struct: [%v]", err)
|
||||
}
|
||||
// Handle both formats for OutputPath
|
||||
if !filepath.IsAbs(loggerConfig.Logging.OutputPath) {
|
||||
if loggerConfig.Logging.OutputPath == "" {
|
||||
loggerConfig.Logging.OutputPath = defaultLoggerOutputFile
|
||||
}
|
||||
// If OutputPath is relative, prepend the logs directory
|
||||
loggerConfig.Logging.OutputPath = filepath.Join(configPath, "logs", loggerConfig.Logging.OutputPath)
|
||||
}
|
||||
logDir := filepath.Dir(loggerConfig.Logging.OutputPath) // Extract the directory
|
||||
// Create the directory if it does not exist
|
||||
if err = os.MkdirAll(logDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create client logs directory: %w", err)
|
||||
}
|
||||
Log, err = createLogger(loggerConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create client logger: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLogger(loggerConfig LoggerConfig) (*zap.Logger, error) {
|
||||
configLevel, err := zapcore.ParseLevel(loggerConfig.Logging.Level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create log file and set the permission
|
||||
file, err := os.OpenFile(loggerConfig.Logging.OutputPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file.Close()
|
||||
|
||||
// configure lumberjack for log rotation
|
||||
logWriter := &lumberjack.Logger{
|
||||
Filename: loggerConfig.Logging.OutputPath,
|
||||
MaxSize: loggerConfig.Logging.File.MaxSize,
|
||||
MaxBackups: loggerConfig.Logging.File.MaxBackups,
|
||||
MaxAge: loggerConfig.Logging.File.MaxAge,
|
||||
Compress: loggerConfig.Logging.File.Compress,
|
||||
}
|
||||
|
||||
// Create cores for different log levels
|
||||
core := zapcore.NewCore(
|
||||
zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
|
||||
MessageKey: loggerConfig.Logging.EncoderConfig.MessageKey,
|
||||
LevelKey: loggerConfig.Logging.EncoderConfig.LevelKey,
|
||||
TimeKey: loggerConfig.Logging.EncoderConfig.TimeKey,
|
||||
NameKey: loggerConfig.Logging.EncoderConfig.NameKey,
|
||||
CallerKey: loggerConfig.Logging.EncoderConfig.CallerKey,
|
||||
StacktraceKey: "", // disable stacktrace
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
ConsoleSeparator: " ",
|
||||
}),
|
||||
zapcore.AddSync(logWriter),
|
||||
configLevel,
|
||||
)
|
||||
logger := zap.New(core,
|
||||
zap.AddCaller(), // caller info
|
||||
zap.AddStacktrace(zap.ErrorLevel), // show error stacktrace
|
||||
zap.AddCallerSkip(1), // skip one layer of caller
|
||||
)
|
||||
return logger, nil
|
||||
|
||||
}
|
||||
|
||||
func Info(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Info(msg, fields...)
|
||||
}
|
||||
func Error(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Error(msg, fields...)
|
||||
}
|
||||
|
||||
func Warn(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Warn(msg, fields...)
|
||||
}
|
||||
|
||||
func Debug(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(true)).Debug(msg, fields...)
|
||||
}
|
||||
|
||||
func Fatal(msg string, fields ...zap.Field) {
|
||||
Log.WithOptions(zap.WithCaller(false)).Fatal(msg, fields...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,92 +1,92 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// validate the specified yaml file format whether is correct
|
||||
// return the content of the yaml file if the format is correct
|
||||
// return error if the format is incorrect
|
||||
func ValidateYamlFormat(yamlFilePath string) ([]byte, error) {
|
||||
// find the file path
|
||||
yamlFilePath, err := filepath.Abs(yamlFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get scheme file path, suppose schema file is in the same directory as yaml file
|
||||
yamlFileRootPath := filepath.Dir(yamlFilePath)
|
||||
realFileName := filepath.Base(yamlFilePath)
|
||||
preRealFileName := realFileName[:len(realFileName)-5] + "_schema.json"
|
||||
schemaFilePath := filepath.Join(yamlFileRootPath, "schema", preRealFileName)
|
||||
// checking if the schema file exists
|
||||
if _, err := os.Stat(schemaFilePath); os.IsNotExist(err) {
|
||||
err = fmt.Errorf("schema file %s does not exist ", schemaFilePath)
|
||||
return nil, err
|
||||
}
|
||||
data, err := ValidateYAML(yamlFilePath, schemaFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ValidateYAML validates the YAML content against the provided JSON schema
|
||||
func ValidateYAML(yamlPath, schemaPath string) ([]byte, error) {
|
||||
// schemaLoader is a absolute file path
|
||||
schemaLoader := gojsonschema.NewReferenceLoader("file:///" + filepath.ToSlash(schemaPath))
|
||||
// get yaml file content by reading the file
|
||||
data, err := os.ReadFile(yamlPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get the yaml file into object
|
||||
var raw interface{}
|
||||
err = yaml.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// transform into map[string]interface{}
|
||||
yamlContent := convertYAML(raw)
|
||||
|
||||
// create documentLoader
|
||||
documentLoader := gojsonschema.NewGoLoader(yamlContent)
|
||||
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !result.Valid() {
|
||||
var errorMessages string
|
||||
for _, desc := range result.Errors() {
|
||||
errorMessages += fmt.Sprintf("- %s\n", desc)
|
||||
}
|
||||
err = errors.New(errorMessages)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// return the yaml content
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// convertYAML recursively converts map[interface{}]interface{} to map[string]interface{}
|
||||
func convertYAML(i interface{}) interface{} {
|
||||
switch x := i.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
m2 := make(map[string]interface{})
|
||||
for k, v := range x {
|
||||
m2[fmt.Sprintf("%v", k)] = convertYAML(v)
|
||||
}
|
||||
return m2
|
||||
case []interface{}:
|
||||
for idx, v := range x {
|
||||
x[idx] = convertYAML(v)
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// validate the specified yaml file format whether is correct
|
||||
// return the content of the yaml file if the format is correct
|
||||
// return error if the format is incorrect
|
||||
func ValidateYamlFormat(yamlFilePath string) ([]byte, error) {
|
||||
// find the file path
|
||||
yamlFilePath, err := filepath.Abs(yamlFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get scheme file path, suppose schema file is in the same directory as yaml file
|
||||
yamlFileRootPath := filepath.Dir(yamlFilePath)
|
||||
realFileName := filepath.Base(yamlFilePath)
|
||||
preRealFileName := realFileName[:len(realFileName)-5] + "_schema.json"
|
||||
schemaFilePath := filepath.Join(yamlFileRootPath, "schema", preRealFileName)
|
||||
// checking if the schema file exists
|
||||
if _, err := os.Stat(schemaFilePath); os.IsNotExist(err) {
|
||||
err = fmt.Errorf("schema file %s does not exist ", schemaFilePath)
|
||||
return nil, err
|
||||
}
|
||||
data, err := ValidateYAML(yamlFilePath, schemaFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ValidateYAML validates the YAML content against the provided JSON schema
|
||||
func ValidateYAML(yamlPath, schemaPath string) ([]byte, error) {
|
||||
// schemaLoader is a absolute file path
|
||||
schemaLoader := gojsonschema.NewReferenceLoader("file:///" + filepath.ToSlash(schemaPath))
|
||||
// get yaml file content by reading the file
|
||||
data, err := os.ReadFile(yamlPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get the yaml file into object
|
||||
var raw interface{}
|
||||
err = yaml.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// transform into map[string]interface{}
|
||||
yamlContent := convertYAML(raw)
|
||||
|
||||
// create documentLoader
|
||||
documentLoader := gojsonschema.NewGoLoader(yamlContent)
|
||||
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !result.Valid() {
|
||||
var errorMessages string
|
||||
for _, desc := range result.Errors() {
|
||||
errorMessages += fmt.Sprintf("- %s\n", desc)
|
||||
}
|
||||
err = errors.New(errorMessages)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// return the yaml content
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// convertYAML recursively converts map[interface{}]interface{} to map[string]interface{}
|
||||
func convertYAML(i interface{}) interface{} {
|
||||
switch x := i.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
m2 := make(map[string]interface{})
|
||||
for k, v := range x {
|
||||
m2[fmt.Sprintf("%v", k)] = convertYAML(v)
|
||||
}
|
||||
return m2
|
||||
case []interface{}:
|
||||
for idx, v := range x {
|
||||
x[idx] = convertYAML(v)
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
package models
|
||||
|
||||
type ClusterResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []ClusterInfo `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ClusterInfo struct {
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
ControllerName string `json:"controller_name"`
|
||||
Resources interface{} `json:"resources"`
|
||||
HostTypes interface{} `json:"host_types"`
|
||||
HostModels interface{} `json:"host_models"`
|
||||
ClusterAdmins interface{} `json:"cluster_admins"`
|
||||
}
|
||||
package models
|
||||
|
||||
type ClusterResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []ClusterInfo `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ClusterInfo struct {
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
ControllerName string `json:"controller_name"`
|
||||
Resources interface{} `json:"resources"`
|
||||
HostTypes interface{} `json:"host_types"`
|
||||
HostModels interface{} `json:"host_models"`
|
||||
ClusterAdmins interface{} `json:"cluster_admins"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,172 +1,172 @@
|
|||
package models
|
||||
|
||||
const (
|
||||
HEADER_HOST_NAME = "HOST_NAME"
|
||||
HEADER_HOST_STATUS = "STATUS"
|
||||
HEADER_HOST_NCPUS = "NCPUS"
|
||||
HEADER_HOST_MAX_SLOT = "MAX_SLOT"
|
||||
HEADER_HOST_NUM_RUN = "NUM_RUN"
|
||||
HEADER_HOST_NUM_SSUSP = "NUM_SSUSP"
|
||||
HEADER_HOST_NUM_USUSP = "NUM_USUSP"
|
||||
HEADER_HOST_HOST_TYPE = "HOST_TYPE"
|
||||
// host details info
|
||||
HEADER_HOST_CPUF = "CPUF"
|
||||
HEADER_HOST_JLU = "JL/U"
|
||||
HEADER_HOST_MAX = "MAX"
|
||||
HEADER_HOST_NJOBS = "NJOBS"
|
||||
HEADER_HOST_RUN = "RUN"
|
||||
HEADER_HOST_SSUSP = "SSUSP"
|
||||
HEADER_HOST_USUSP = "USUSP"
|
||||
HEADER_HOST_RSV = "RSV"
|
||||
HEADER_HOST_DISPT_WINDOW = "DISPATCH_WINDOW"
|
||||
// second line
|
||||
HEADER_HOST_R15S = "r15s"
|
||||
HEADER_HOST_R1M = "r1m"
|
||||
HEADER_HOST_R15M = "r15m"
|
||||
HEADER_HOST_UT = "ut"
|
||||
HEADER_HOST_PG = "pg"
|
||||
HEADER_HOST_IO = "io"
|
||||
HEADER_HOST_LS = "ls"
|
||||
HEADER_HOST_IT = "it"
|
||||
HEADER_HOST_TMP = "tmp"
|
||||
HEADER_HOST_SWP = "swp"
|
||||
HEADER_HOST_MEM = "mem"
|
||||
HEADER_HOST_SLOTS = "slots"
|
||||
)
|
||||
|
||||
var HostDetailHeaders = []string{
|
||||
HEADER_HOST_STATUS,
|
||||
HEADER_HOST_CPUF,
|
||||
HEADER_HOST_JLU,
|
||||
HEADER_HOST_MAX,
|
||||
HEADER_HOST_NJOBS,
|
||||
HEADER_HOST_RUN,
|
||||
HEADER_HOST_SSUSP,
|
||||
HEADER_HOST_USUSP,
|
||||
HEADER_HOST_RSV,
|
||||
HEADER_HOST_DISPT_WINDOW,
|
||||
}
|
||||
|
||||
var HostLoadSchedulingHeaders = []string{
|
||||
HEADER_HOST_R15S,
|
||||
HEADER_HOST_R1M,
|
||||
HEADER_HOST_R15M,
|
||||
HEADER_HOST_UT,
|
||||
HEADER_HOST_PG,
|
||||
HEADER_HOST_IO,
|
||||
HEADER_HOST_LS,
|
||||
HEADER_HOST_IT,
|
||||
HEADER_HOST_TMP,
|
||||
HEADER_HOST_SWP,
|
||||
HEADER_HOST_MEM,
|
||||
HEADER_HOST_SLOTS,
|
||||
}
|
||||
|
||||
// HostsResponse define the data structure in the host query response
|
||||
type HostsResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []XCEHost `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Host define the data structure in the host info\
|
||||
type XCEHost struct {
|
||||
HostID int64 `json:"host_id"`
|
||||
HostName string `json:"host_name"`
|
||||
HostStatus string `json:"host_status"`
|
||||
HostModel string `json:"host_model"`
|
||||
HostType string `json:"host_type"`
|
||||
HostGroup string `json:"host_group"`
|
||||
Flag int `json:"flag"`
|
||||
CPUFactor float64 `json:"cpu_factor"`
|
||||
DisptWindow interface{} `json:"dispt_window"`
|
||||
NumCPUs int `json:"num_cpus"`
|
||||
MaxSlot int `json:"max_slot"`
|
||||
MaxMem int `json:"max_mem"`
|
||||
MaxSwap int `json:"max_swap"`
|
||||
MaxTmp int `json:"max_tmp"`
|
||||
NumDisk int `json:"num_disk"`
|
||||
NumRun int `json:"num_run"`
|
||||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
NumDone int `json:"num_done"`
|
||||
AffTopo interface{} `json:"aff_topo"` // JSONB
|
||||
GPUTopo interface{} `json:"gpu_topo"` // JSONB
|
||||
SchedStop SchedStop `json:"sched_stop"` // JSONB
|
||||
RealLoad interface{} `json:"real_load"`
|
||||
|
||||
// ResourceValues carries this host's live per-resource wire values
|
||||
// (r15s/r1m/r15m/ut/pg/io/ls/it/tmp/swp/mem/slots, among others), the
|
||||
// same set the C scheduler's own "info worker" REPL command shows --
|
||||
// populated by the apiserver from the worker's own resval pushes.
|
||||
ResourceValues []ResourceDistributionInfo `json:"resource_values"`
|
||||
}
|
||||
|
||||
// ResourceDistributionInfo mirrors apiserver/models.ResourceDistributionInfo's
|
||||
// JSON shape -- one resource's current value for one host/worker.
|
||||
type ResourceDistributionInfo struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
IsDynamic bool `json:"is_dynamic"`
|
||||
ValueType int `json:"value_type"`
|
||||
BooleanValue bool `json:"boolean_value"`
|
||||
StringValue string `json:"string_value"`
|
||||
NumericValue float64 `json:"numeric_value"`
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
var HostFieldsMap = map[string]string{
|
||||
HEADER_HOST_NAME: "host_name",
|
||||
HEADER_HOST_STATUS: "host_status",
|
||||
HEADER_HOST_NCPUS: "num_cores",
|
||||
HEADER_HOST_MAX_SLOT: "max_slot",
|
||||
HEADER_HOST_NUM_RUN: "num_run",
|
||||
HEADER_HOST_NUM_SSUSP: "num_ssusp",
|
||||
HEADER_HOST_NUM_USUSP: "num_ususp",
|
||||
HEADER_HOST_HOST_TYPE: "host_type",
|
||||
}
|
||||
|
||||
type SchedStop struct {
|
||||
Ls string `json:"ls"` // Load Sharing
|
||||
Pg string `json:"pg"` // Page
|
||||
R1m string `json:"r1m"` // Recent 1 minute
|
||||
Tmp string `json:"tmp"` // Temporary
|
||||
}
|
||||
|
||||
type HostControlRequest struct {
|
||||
ControlAction string `json:"action" binding:"required"` //control action
|
||||
Hostname []string `json:"host_name" binding:"required"` //host name
|
||||
Comment string `json:"comment"` //comment
|
||||
}
|
||||
|
||||
type HostControlResponse struct {
|
||||
Data struct {
|
||||
Accepted []*HostControlResponseItem `json:"accepted"`
|
||||
Failed []*HostControlResponseItem `json:"failed"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type HostControlResponseItem struct {
|
||||
HostName string `json:"host_name"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func (h *XCEHost) UnmarshalJSON(data []byte) error {
|
||||
type Alias XCEHost
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*h = XCEHost(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResReqSelectRequest struct {
|
||||
Select string `json:"select"`
|
||||
Order string `json:"order,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
PageToken string `json:"page_token,omitempty"`
|
||||
}
|
||||
package models
|
||||
|
||||
const (
|
||||
HEADER_HOST_NAME = "HOST_NAME"
|
||||
HEADER_HOST_STATUS = "STATUS"
|
||||
HEADER_HOST_NCPUS = "NCPUS"
|
||||
HEADER_HOST_MAX_SLOT = "MAX_SLOT"
|
||||
HEADER_HOST_NUM_RUN = "NUM_RUN"
|
||||
HEADER_HOST_NUM_SSUSP = "NUM_SSUSP"
|
||||
HEADER_HOST_NUM_USUSP = "NUM_USUSP"
|
||||
HEADER_HOST_HOST_TYPE = "HOST_TYPE"
|
||||
// host details info
|
||||
HEADER_HOST_CPUF = "CPUF"
|
||||
HEADER_HOST_JLU = "JL/U"
|
||||
HEADER_HOST_MAX = "MAX"
|
||||
HEADER_HOST_NJOBS = "NJOBS"
|
||||
HEADER_HOST_RUN = "RUN"
|
||||
HEADER_HOST_SSUSP = "SSUSP"
|
||||
HEADER_HOST_USUSP = "USUSP"
|
||||
HEADER_HOST_RSV = "RSV"
|
||||
HEADER_HOST_DISPT_WINDOW = "DISPATCH_WINDOW"
|
||||
// second line
|
||||
HEADER_HOST_R15S = "r15s"
|
||||
HEADER_HOST_R1M = "r1m"
|
||||
HEADER_HOST_R15M = "r15m"
|
||||
HEADER_HOST_UT = "ut"
|
||||
HEADER_HOST_PG = "pg"
|
||||
HEADER_HOST_IO = "io"
|
||||
HEADER_HOST_LS = "ls"
|
||||
HEADER_HOST_IT = "it"
|
||||
HEADER_HOST_TMP = "tmp"
|
||||
HEADER_HOST_SWP = "swp"
|
||||
HEADER_HOST_MEM = "mem"
|
||||
HEADER_HOST_SLOTS = "slots"
|
||||
)
|
||||
|
||||
var HostDetailHeaders = []string{
|
||||
HEADER_HOST_STATUS,
|
||||
HEADER_HOST_CPUF,
|
||||
HEADER_HOST_JLU,
|
||||
HEADER_HOST_MAX,
|
||||
HEADER_HOST_NJOBS,
|
||||
HEADER_HOST_RUN,
|
||||
HEADER_HOST_SSUSP,
|
||||
HEADER_HOST_USUSP,
|
||||
HEADER_HOST_RSV,
|
||||
HEADER_HOST_DISPT_WINDOW,
|
||||
}
|
||||
|
||||
var HostLoadSchedulingHeaders = []string{
|
||||
HEADER_HOST_R15S,
|
||||
HEADER_HOST_R1M,
|
||||
HEADER_HOST_R15M,
|
||||
HEADER_HOST_UT,
|
||||
HEADER_HOST_PG,
|
||||
HEADER_HOST_IO,
|
||||
HEADER_HOST_LS,
|
||||
HEADER_HOST_IT,
|
||||
HEADER_HOST_TMP,
|
||||
HEADER_HOST_SWP,
|
||||
HEADER_HOST_MEM,
|
||||
HEADER_HOST_SLOTS,
|
||||
}
|
||||
|
||||
// HostsResponse define the data structure in the host query response
|
||||
type HostsResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []XCEHost `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Host define the data structure in the host info\
|
||||
type XCEHost struct {
|
||||
HostID int64 `json:"host_id"`
|
||||
HostName string `json:"host_name"`
|
||||
HostStatus string `json:"host_status"`
|
||||
HostModel string `json:"host_model"`
|
||||
HostType string `json:"host_type"`
|
||||
HostGroup string `json:"host_group"`
|
||||
Flag int `json:"flag"`
|
||||
CPUFactor float64 `json:"cpu_factor"`
|
||||
DisptWindow interface{} `json:"dispt_window"`
|
||||
NumCPUs int `json:"num_cpus"`
|
||||
MaxSlot int `json:"max_slot"`
|
||||
MaxMem int `json:"max_mem"`
|
||||
MaxSwap int `json:"max_swap"`
|
||||
MaxTmp int `json:"max_tmp"`
|
||||
NumDisk int `json:"num_disk"`
|
||||
NumRun int `json:"num_run"`
|
||||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
NumDone int `json:"num_done"`
|
||||
AffTopo interface{} `json:"aff_topo"` // JSONB
|
||||
GPUTopo interface{} `json:"gpu_topo"` // JSONB
|
||||
SchedStop SchedStop `json:"sched_stop"` // JSONB
|
||||
RealLoad interface{} `json:"real_load"`
|
||||
|
||||
// ResourceValues carries this host's live per-resource wire values
|
||||
// (r15s/r1m/r15m/ut/pg/io/ls/it/tmp/swp/mem/slots, among others), the
|
||||
// same set the C scheduler's own "info worker" REPL command shows --
|
||||
// populated by the apiserver from the worker's own resval pushes.
|
||||
ResourceValues []ResourceDistributionInfo `json:"resource_values"`
|
||||
}
|
||||
|
||||
// ResourceDistributionInfo mirrors apiserver/models.ResourceDistributionInfo's
|
||||
// JSON shape -- one resource's current value for one host/worker.
|
||||
type ResourceDistributionInfo struct {
|
||||
ResourceName string `json:"res_name"`
|
||||
IsDynamic bool `json:"is_dynamic"`
|
||||
ValueType int `json:"value_type"`
|
||||
BooleanValue bool `json:"boolean_value"`
|
||||
StringValue string `json:"string_value"`
|
||||
NumericValue float64 `json:"numeric_value"`
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
var HostFieldsMap = map[string]string{
|
||||
HEADER_HOST_NAME: "host_name",
|
||||
HEADER_HOST_STATUS: "host_status",
|
||||
HEADER_HOST_NCPUS: "num_cores",
|
||||
HEADER_HOST_MAX_SLOT: "max_slot",
|
||||
HEADER_HOST_NUM_RUN: "num_run",
|
||||
HEADER_HOST_NUM_SSUSP: "num_ssusp",
|
||||
HEADER_HOST_NUM_USUSP: "num_ususp",
|
||||
HEADER_HOST_HOST_TYPE: "host_type",
|
||||
}
|
||||
|
||||
type SchedStop struct {
|
||||
Ls string `json:"ls"` // Load Sharing
|
||||
Pg string `json:"pg"` // Page
|
||||
R1m string `json:"r1m"` // Recent 1 minute
|
||||
Tmp string `json:"tmp"` // Temporary
|
||||
}
|
||||
|
||||
type HostControlRequest struct {
|
||||
ControlAction string `json:"action" binding:"required"` //control action
|
||||
Hostname []string `json:"host_name" binding:"required"` //host name
|
||||
Comment string `json:"comment"` //comment
|
||||
}
|
||||
|
||||
type HostControlResponse struct {
|
||||
Data struct {
|
||||
Accepted []*HostControlResponseItem `json:"accepted"`
|
||||
Failed []*HostControlResponseItem `json:"failed"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type HostControlResponseItem struct {
|
||||
HostName string `json:"host_name"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func (h *XCEHost) UnmarshalJSON(data []byte) error {
|
||||
type Alias XCEHost
|
||||
var a Alias
|
||||
if err := UnmarshalOrderMapRow(data, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
*h = XCEHost(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResReqSelectRequest struct {
|
||||
Select string `json:"select"`
|
||||
Order string `json:"order,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
PageToken string `json:"page_token,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ var JobLoadSchedulingHeaders = []string{
|
|||
HEADER_JOB_MEM,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// for array jobs
|
||||
const (
|
||||
HEADER_ARRAY_JOB_JOBID = "JOBID"
|
||||
|
|
@ -93,96 +91,96 @@ type Jsonb struct {
|
|||
|
||||
// Job define the data structure in the job info
|
||||
type QueryJob struct {
|
||||
JobID int64 `json:"job_id"`
|
||||
SubmitUser string `json:"submit_user"`
|
||||
JobStatus string `json:"job_status"`
|
||||
JobName string `json:"job_name"`
|
||||
Command string `json:"command"`
|
||||
QueueName string `json:"queue_name"`
|
||||
ProjectName string `json:"project_name"`
|
||||
UserGroup string `json:"user_group"`
|
||||
LicenseProject string `json:"license_project"`
|
||||
JobDescription string `json:"job_description"`
|
||||
Resreq string `json:"resreq"`
|
||||
RsvID string `json:"rsv_id"`
|
||||
SLA string `json:"sla"`
|
||||
MaxNumProcessors int `json:"maxnum_processors"`
|
||||
NumProcessors int `json:"num_processors"`
|
||||
AskedHosts string `json:"asked_hosts"`
|
||||
BeginTime string `json:"begin_time"`
|
||||
TermTime string `json:"term_time"`
|
||||
UserPriority int `json:"user_priority"`
|
||||
DependCond string `json:"depend_cond"`
|
||||
SubmitTime int64 `json:"submit_time"`
|
||||
ReserveTime int64 `json:"reserve_time"`
|
||||
StartTime int64 `json:"start_time"`
|
||||
EndTime int64 `json:"end_time"`
|
||||
JobRusage string `json:"job_rusage"`
|
||||
MaxMem float64 `json:"max_mem"`
|
||||
AvgMem float64 `json:"avg_mem"`
|
||||
UsedMem float64 `json:"used_mem"`
|
||||
CPUTime int `json:"cpu_time"`
|
||||
PeakCPUTime int `json:"peak_cpu_time"`
|
||||
PeakCPUDuration float64 `json:"peak_cpu_duration"`
|
||||
MemEfficiency float64 `json:"mem_efficiency"` // Memory efficiency in percentage
|
||||
AvgCPUEfficiency float64 `json:"avg_cpu_efficiency"` // Average CPU efficiency in percentage
|
||||
PeakCPUEfficiency float64 `json:"peak_cpu_efficiency"` // Peak CPU efficiency in percentage
|
||||
NumHosts int `json:"num_hosts"` // Number of hosts used by the job
|
||||
FirstHost string `json:"first_host"` // First host where the job ran
|
||||
AllocSlot string `json:"alloc_slots"` // Allocated slot information
|
||||
Duration int `json:"duration"`
|
||||
Umask int `json:"umask"`
|
||||
CWD string `json:"cwd"`
|
||||
SubmitHomeDir string `json:"submit_home_dir"`
|
||||
FromHost string `json:"from_host"`
|
||||
ExecHosts ExecHosts `json:"exec_hosts"`
|
||||
JobID int64 `json:"job_id"`
|
||||
SubmitUser string `json:"submit_user"`
|
||||
JobStatus string `json:"job_status"`
|
||||
JobName string `json:"job_name"`
|
||||
Command string `json:"command"`
|
||||
QueueName string `json:"queue_name"`
|
||||
ProjectName string `json:"project_name"`
|
||||
UserGroup string `json:"user_group"`
|
||||
LicenseProject string `json:"license_project"`
|
||||
JobDescription string `json:"job_description"`
|
||||
Resreq string `json:"resreq"`
|
||||
RsvID string `json:"rsv_id"`
|
||||
SLA string `json:"sla"`
|
||||
MaxNumProcessors int `json:"maxnum_processors"`
|
||||
NumProcessors int `json:"num_processors"`
|
||||
AskedHosts string `json:"asked_hosts"`
|
||||
BeginTime string `json:"begin_time"`
|
||||
TermTime string `json:"term_time"`
|
||||
UserPriority int `json:"user_priority"`
|
||||
DependCond string `json:"depend_cond"`
|
||||
SubmitTime int64 `json:"submit_time"`
|
||||
ReserveTime int64 `json:"reserve_time"`
|
||||
StartTime int64 `json:"start_time"`
|
||||
EndTime int64 `json:"end_time"`
|
||||
JobRusage string `json:"job_rusage"`
|
||||
MaxMem float64 `json:"max_mem"`
|
||||
AvgMem float64 `json:"avg_mem"`
|
||||
UsedMem float64 `json:"used_mem"`
|
||||
CPUTime int `json:"cpu_time"`
|
||||
PeakCPUTime int `json:"peak_cpu_time"`
|
||||
PeakCPUDuration float64 `json:"peak_cpu_duration"`
|
||||
MemEfficiency float64 `json:"mem_efficiency"` // Memory efficiency in percentage
|
||||
AvgCPUEfficiency float64 `json:"avg_cpu_efficiency"` // Average CPU efficiency in percentage
|
||||
PeakCPUEfficiency float64 `json:"peak_cpu_efficiency"` // Peak CPU efficiency in percentage
|
||||
NumHosts int `json:"num_hosts"` // Number of hosts used by the job
|
||||
FirstHost string `json:"first_host"` // First host where the job ran
|
||||
AllocSlot string `json:"alloc_slots"` // Allocated slot information
|
||||
Duration int `json:"duration"`
|
||||
Umask int `json:"umask"`
|
||||
CWD string `json:"cwd"`
|
||||
SubmitHomeDir string `json:"submit_home_dir"`
|
||||
FromHost string `json:"from_host"`
|
||||
ExecHosts ExecHosts `json:"exec_hosts"`
|
||||
ExitStatus ExitStatusValue `json:"exit_status"`
|
||||
ExecUID int `json:"exec_uid"`
|
||||
ExecHome string `json:"exec_home"`
|
||||
ExecCWD string `json:"exec_cwd"`
|
||||
ExecUserName string `json:"exec_user_name"`
|
||||
JobPriority int `json:"job_priority"`
|
||||
ChargedSAAP string `json:"charged_SAAP"`
|
||||
PreExecCmd string `json:"pre_exec_cmd"`
|
||||
PostExecCmd string `json:"post_exec_cmd"`
|
||||
RequeueErrorValues string `json:"requeue_error_values"`
|
||||
NumLicenses int `json:"num_licenses"`
|
||||
LicenseNames []string `json:"license_names"`
|
||||
RunTime int `json:"run_time"`
|
||||
RunTimeEstimation int `json:"run_time_estimation"`
|
||||
MigThreshold int `json:"mig_threshold"`
|
||||
ChkpntPeriod int `json:"chkpnt_period"`
|
||||
ChkpntDir string `json:"chkpnt_dir"`
|
||||
OutDir string `json:"out_dir"`
|
||||
InFile string `json:"in_file"`
|
||||
OutFile string `json:"out_file"`
|
||||
ErrFile string `json:"err_file"`
|
||||
MailUser string `json:"mail_user"`
|
||||
LoginShell string `json:"login_shell"`
|
||||
ExceptList string `json:"except_list"`
|
||||
RLimits64 []int64 `json:"rlimits64"`
|
||||
SubEnvVars string `json:"sub_env_vars"`
|
||||
PendingReason string `json:"pending_reason"`
|
||||
JobStartInfo string `json:"job_start_info"`
|
||||
JobExecInfo string `json:"job_exec_info"`
|
||||
CoreLimit float64 `json:"core_limit"`
|
||||
CPULimit string `json:"cpu_limit"`
|
||||
RunLimit string `json:"run_limit"`
|
||||
FileLimit float64 `json:"file_limit"`
|
||||
DataLimit float64 `json:"data_limit"`
|
||||
StackLimit float64 `json:"stack_limit"`
|
||||
MemLimit float64 `json:"mem_limit"`
|
||||
SwapLimit float64 `json:"swap_limit"`
|
||||
ProcessLimit float64 `json:"process_limit"`
|
||||
ThreadLimit float64 `json:"thread_limit"`
|
||||
NumFiles int `json:"num_files"`
|
||||
ArrayIdx int `json:"array_idx"` //
|
||||
UniqueJobID int64 `json:"unique_job_id"` //
|
||||
ExtendData JobExtendData `json:"extend_data"`
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
PGIDs IntSlice `json:"pgids"`
|
||||
PID IntSlice `json:"pids"`
|
||||
LaunchType string `json:"launch_type"`
|
||||
ExecUID int `json:"exec_uid"`
|
||||
ExecHome string `json:"exec_home"`
|
||||
ExecCWD string `json:"exec_cwd"`
|
||||
ExecUserName string `json:"exec_user_name"`
|
||||
JobPriority int `json:"job_priority"`
|
||||
ChargedSAAP string `json:"charged_SAAP"`
|
||||
PreExecCmd string `json:"pre_exec_cmd"`
|
||||
PostExecCmd string `json:"post_exec_cmd"`
|
||||
RequeueErrorValues string `json:"requeue_error_values"`
|
||||
NumLicenses int `json:"num_licenses"`
|
||||
LicenseNames []string `json:"license_names"`
|
||||
RunTime int `json:"run_time"`
|
||||
RunTimeEstimation int `json:"run_time_estimation"`
|
||||
MigThreshold int `json:"mig_threshold"`
|
||||
ChkpntPeriod int `json:"chkpnt_period"`
|
||||
ChkpntDir string `json:"chkpnt_dir"`
|
||||
OutDir string `json:"out_dir"`
|
||||
InFile string `json:"in_file"`
|
||||
OutFile string `json:"out_file"`
|
||||
ErrFile string `json:"err_file"`
|
||||
MailUser string `json:"mail_user"`
|
||||
LoginShell string `json:"login_shell"`
|
||||
ExceptList string `json:"except_list"`
|
||||
RLimits64 []int64 `json:"rlimits64"`
|
||||
SubEnvVars string `json:"sub_env_vars"`
|
||||
PendingReason string `json:"pending_reason"`
|
||||
JobStartInfo string `json:"job_start_info"`
|
||||
JobExecInfo string `json:"job_exec_info"`
|
||||
CoreLimit float64 `json:"core_limit"`
|
||||
CPULimit string `json:"cpu_limit"`
|
||||
RunLimit string `json:"run_limit"`
|
||||
FileLimit float64 `json:"file_limit"`
|
||||
DataLimit float64 `json:"data_limit"`
|
||||
StackLimit float64 `json:"stack_limit"`
|
||||
MemLimit float64 `json:"mem_limit"`
|
||||
SwapLimit float64 `json:"swap_limit"`
|
||||
ProcessLimit float64 `json:"process_limit"`
|
||||
ThreadLimit float64 `json:"thread_limit"`
|
||||
NumFiles int `json:"num_files"`
|
||||
ArrayIdx int `json:"array_idx"` //
|
||||
UniqueJobID int64 `json:"unique_job_id"` //
|
||||
ExtendData JobExtendData `json:"extend_data"`
|
||||
InteractiveJobURL string `json:"interactive_job_url"`
|
||||
PGIDs IntSlice `json:"pgids"`
|
||||
PID IntSlice `json:"pids"`
|
||||
LaunchType string `json:"launch_type"`
|
||||
}
|
||||
|
||||
var JobFieldsMap = map[string]string{
|
||||
|
|
|
|||
|
|
@ -1,124 +1,124 @@
|
|||
package models
|
||||
|
||||
const (
|
||||
HEADER_QUEUE_NAME = "QUEUE_NAME"
|
||||
HEADER_QUEUE_PRIORITY = "PRIORITY"
|
||||
HEADER_QUEUE_STATUS = "QUEUE_STATUS"
|
||||
HEADER_QUEUE_NUM_PEND = "NUM_PEND"
|
||||
HEADER_QUEUE_NUM_RUN = "NUM_RUN"
|
||||
HEADER_QUEUE_NUM_SSUSP = "NUM_SSUSP"
|
||||
HEADER_QUEUE_NUM_USUSP = "NUM_USUSP"
|
||||
HEADER_QUEUE_NUM_DONE = "NUM_DONE"
|
||||
// queue detail info
|
||||
HEADER_QUEUE_PRIO = "PRIO"
|
||||
HEADER_QUEUE_NICE = "NICE"
|
||||
HEADER_QUEUE_MAX = "MAX"
|
||||
HEADER_QUEUE_JLU = "JL/U"
|
||||
HEADER_QUEUE_JLP = "JL/P"
|
||||
HEADER_QUEUE_JLH = "JL/H"
|
||||
HEADER_QUEUE_NJOBS = "NJOBS"
|
||||
HEADER_QUEUE_PEND = "PEND"
|
||||
HEADER_QUEUE_RUN = "RUN"
|
||||
HEADER_QUEUE_SSUSP = "SSUSP"
|
||||
HEADER_QUEUE_USUSP = "USUSP"
|
||||
HEADER_QUEUE_RSV = "RSV"
|
||||
// second line
|
||||
HEADER_QUEUE_R15S = "r15s"
|
||||
HEADER_QUEUE_R1M = "r1m"
|
||||
HEADER_QUEUE_R15M = "r15m"
|
||||
HEADER_QUEUE_UT = "ut"
|
||||
HEADER_QUEUE_PG = "pg"
|
||||
HEADER_QUEUE_IO = "io"
|
||||
HEADER_QUEUE_LS = "ls"
|
||||
HEADER_QUEUE_IT = "it"
|
||||
HEADER_QUEUE_TMP = "tmp"
|
||||
HEADER_QUEUE_SWP = "swp"
|
||||
HEADER_QUEUE_MEM = "mem"
|
||||
)
|
||||
|
||||
var QueueDetailHeaders = []string{
|
||||
HEADER_QUEUE_PRIO,
|
||||
HEADER_QUEUE_NICE,
|
||||
HEADER_QUEUE_STATUS,
|
||||
HEADER_QUEUE_MAX,
|
||||
HEADER_QUEUE_JLU,
|
||||
HEADER_QUEUE_JLP,
|
||||
HEADER_QUEUE_JLH,
|
||||
HEADER_QUEUE_NJOBS,
|
||||
HEADER_QUEUE_PEND,
|
||||
HEADER_QUEUE_RUN,
|
||||
HEADER_QUEUE_SSUSP,
|
||||
HEADER_QUEUE_USUSP,
|
||||
HEADER_QUEUE_RSV,
|
||||
}
|
||||
|
||||
var QueueLoadSchedulingHeaders = []string{
|
||||
HEADER_QUEUE_R15S,
|
||||
HEADER_QUEUE_R1M,
|
||||
HEADER_QUEUE_R15M,
|
||||
HEADER_QUEUE_UT,
|
||||
HEADER_QUEUE_PG,
|
||||
HEADER_QUEUE_IO,
|
||||
HEADER_QUEUE_LS,
|
||||
HEADER_QUEUE_IT,
|
||||
HEADER_QUEUE_TMP,
|
||||
HEADER_QUEUE_SWP,
|
||||
HEADER_QUEUE_MEM,
|
||||
}
|
||||
|
||||
// QueuesResponse define the data structure in the queue query response
|
||||
type QueuesResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []DBQueueInfo `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
var QueueFieldsMap = map[string]string{
|
||||
HEADER_QUEUE_NAME: "queue_name",
|
||||
HEADER_QUEUE_PRIORITY: "priority",
|
||||
HEADER_QUEUE_STATUS: "queue_status",
|
||||
HEADER_QUEUE_NUM_PEND: "num_pend",
|
||||
HEADER_QUEUE_NUM_RUN: "num_run",
|
||||
HEADER_QUEUE_NUM_SSUSP: "num_ssusp",
|
||||
HEADER_QUEUE_NUM_USUSP: "num_ususp",
|
||||
HEADER_QUEUE_NUM_DONE: "num_done",
|
||||
}
|
||||
|
||||
// DBQueueInfo define the data structure in the queue info
|
||||
type DBQueueInfo struct {
|
||||
QueueName string `json:"queue_name"`
|
||||
QueueStatus string `json:"queue_status"`
|
||||
Priority int `json:"priority"`
|
||||
Description string `json:"description"`
|
||||
NumPend int `json:"num_pend"`
|
||||
NumRun int `json:"num_run"`
|
||||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
NumDone int `json:"num_done"`
|
||||
FSInfo map[string]interface{} `json:"fs_info"` // JSONB
|
||||
}
|
||||
|
||||
type QueueControlRequest struct {
|
||||
ControlAction string `json:"action" binding:"required"` //control action
|
||||
QueueName []string `json:"queue_name" binding:"required"` //queue name
|
||||
Comment string `json:"comment"` //comment
|
||||
}
|
||||
|
||||
type QueueControlResponse struct {
|
||||
Data struct {
|
||||
Accepted []*QueueControlResponseItem `json:"accepted"`
|
||||
Failed []*QueueControlResponseItem `json:"failed"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type QueueControlResponseItem struct {
|
||||
QueueName string `json:"queue_name"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
package models
|
||||
|
||||
const (
|
||||
HEADER_QUEUE_NAME = "QUEUE_NAME"
|
||||
HEADER_QUEUE_PRIORITY = "PRIORITY"
|
||||
HEADER_QUEUE_STATUS = "QUEUE_STATUS"
|
||||
HEADER_QUEUE_NUM_PEND = "NUM_PEND"
|
||||
HEADER_QUEUE_NUM_RUN = "NUM_RUN"
|
||||
HEADER_QUEUE_NUM_SSUSP = "NUM_SSUSP"
|
||||
HEADER_QUEUE_NUM_USUSP = "NUM_USUSP"
|
||||
HEADER_QUEUE_NUM_DONE = "NUM_DONE"
|
||||
// queue detail info
|
||||
HEADER_QUEUE_PRIO = "PRIO"
|
||||
HEADER_QUEUE_NICE = "NICE"
|
||||
HEADER_QUEUE_MAX = "MAX"
|
||||
HEADER_QUEUE_JLU = "JL/U"
|
||||
HEADER_QUEUE_JLP = "JL/P"
|
||||
HEADER_QUEUE_JLH = "JL/H"
|
||||
HEADER_QUEUE_NJOBS = "NJOBS"
|
||||
HEADER_QUEUE_PEND = "PEND"
|
||||
HEADER_QUEUE_RUN = "RUN"
|
||||
HEADER_QUEUE_SSUSP = "SSUSP"
|
||||
HEADER_QUEUE_USUSP = "USUSP"
|
||||
HEADER_QUEUE_RSV = "RSV"
|
||||
// second line
|
||||
HEADER_QUEUE_R15S = "r15s"
|
||||
HEADER_QUEUE_R1M = "r1m"
|
||||
HEADER_QUEUE_R15M = "r15m"
|
||||
HEADER_QUEUE_UT = "ut"
|
||||
HEADER_QUEUE_PG = "pg"
|
||||
HEADER_QUEUE_IO = "io"
|
||||
HEADER_QUEUE_LS = "ls"
|
||||
HEADER_QUEUE_IT = "it"
|
||||
HEADER_QUEUE_TMP = "tmp"
|
||||
HEADER_QUEUE_SWP = "swp"
|
||||
HEADER_QUEUE_MEM = "mem"
|
||||
)
|
||||
|
||||
var QueueDetailHeaders = []string{
|
||||
HEADER_QUEUE_PRIO,
|
||||
HEADER_QUEUE_NICE,
|
||||
HEADER_QUEUE_STATUS,
|
||||
HEADER_QUEUE_MAX,
|
||||
HEADER_QUEUE_JLU,
|
||||
HEADER_QUEUE_JLP,
|
||||
HEADER_QUEUE_JLH,
|
||||
HEADER_QUEUE_NJOBS,
|
||||
HEADER_QUEUE_PEND,
|
||||
HEADER_QUEUE_RUN,
|
||||
HEADER_QUEUE_SSUSP,
|
||||
HEADER_QUEUE_USUSP,
|
||||
HEADER_QUEUE_RSV,
|
||||
}
|
||||
|
||||
var QueueLoadSchedulingHeaders = []string{
|
||||
HEADER_QUEUE_R15S,
|
||||
HEADER_QUEUE_R1M,
|
||||
HEADER_QUEUE_R15M,
|
||||
HEADER_QUEUE_UT,
|
||||
HEADER_QUEUE_PG,
|
||||
HEADER_QUEUE_IO,
|
||||
HEADER_QUEUE_LS,
|
||||
HEADER_QUEUE_IT,
|
||||
HEADER_QUEUE_TMP,
|
||||
HEADER_QUEUE_SWP,
|
||||
HEADER_QUEUE_MEM,
|
||||
}
|
||||
|
||||
// QueuesResponse define the data structure in the queue query response
|
||||
type QueuesResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Count int `json:"count"`
|
||||
Rows []DBQueueInfo `json:"rows"`
|
||||
Page_token string `json:"page_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
var QueueFieldsMap = map[string]string{
|
||||
HEADER_QUEUE_NAME: "queue_name",
|
||||
HEADER_QUEUE_PRIORITY: "priority",
|
||||
HEADER_QUEUE_STATUS: "queue_status",
|
||||
HEADER_QUEUE_NUM_PEND: "num_pend",
|
||||
HEADER_QUEUE_NUM_RUN: "num_run",
|
||||
HEADER_QUEUE_NUM_SSUSP: "num_ssusp",
|
||||
HEADER_QUEUE_NUM_USUSP: "num_ususp",
|
||||
HEADER_QUEUE_NUM_DONE: "num_done",
|
||||
}
|
||||
|
||||
// DBQueueInfo define the data structure in the queue info
|
||||
type DBQueueInfo struct {
|
||||
QueueName string `json:"queue_name"`
|
||||
QueueStatus string `json:"queue_status"`
|
||||
Priority int `json:"priority"`
|
||||
Description string `json:"description"`
|
||||
NumPend int `json:"num_pend"`
|
||||
NumRun int `json:"num_run"`
|
||||
NumSSusp int `json:"num_ssusp"`
|
||||
NumUSusp int `json:"num_ususp"`
|
||||
NumDone int `json:"num_done"`
|
||||
FSInfo map[string]interface{} `json:"fs_info"` // JSONB
|
||||
}
|
||||
|
||||
type QueueControlRequest struct {
|
||||
ControlAction string `json:"action" binding:"required"` //control action
|
||||
QueueName []string `json:"queue_name" binding:"required"` //queue name
|
||||
Comment string `json:"comment"` //comment
|
||||
}
|
||||
|
||||
type QueueControlResponse struct {
|
||||
Data struct {
|
||||
Accepted []*QueueControlResponseItem `json:"accepted"`
|
||||
Failed []*QueueControlResponseItem `json:"failed"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type QueueControlResponseItem struct {
|
||||
QueueName string `json:"queue_name"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func (q *DBQueueInfo) UnmarshalJSON(data []byte) error {
|
||||
type Alias DBQueueInfo
|
||||
|
|
|
|||
|
|
@ -1,117 +1,117 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
apiserver "xcecli/cmd/auth"
|
||||
"xcecli/cmd/clusters"
|
||||
"xcecli/cmd/hosts"
|
||||
"xcecli/cmd/jobs"
|
||||
"xcecli/cmd/permissions"
|
||||
"xcecli/cmd/queues"
|
||||
"xcecli/cmd/resources"
|
||||
"xcecli/cmd/roles"
|
||||
"xcecli/cmd/users"
|
||||
"xcecli/common/request"
|
||||
"xcecli/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "xce",
|
||||
Short: "XCE CLI tool for the API Server operations",
|
||||
}
|
||||
configManager *config.ConfigManager
|
||||
)
|
||||
|
||||
// execute the root command
|
||||
func Execute(cm *config.ConfigManager, version string) error {
|
||||
if cm == nil {
|
||||
return fmt.Errorf("the config manager is not initialized")
|
||||
}
|
||||
configManager = cm
|
||||
rootCmd.Version = version
|
||||
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||
rootCmd.SilenceErrors = true
|
||||
rootCmd.SilenceUsage = true
|
||||
|
||||
// initialize the subcommands
|
||||
initCommands()
|
||||
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
// withAuth wraps a command with authentication check
|
||||
// This function creates a middleware that checks authentication before executing any command
|
||||
func withAuth(cmd *cobra.Command) *cobra.Command {
|
||||
// Store original handlers
|
||||
originalRunE := cmd.RunE
|
||||
originalRun := cmd.Run
|
||||
|
||||
// Create new RunE that checks authentication first
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
// Check authentication before executing the command
|
||||
_, err := configManager.IsAuthenticated()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute the original command logic
|
||||
if originalRunE != nil {
|
||||
return originalRunE(cmd, args)
|
||||
}
|
||||
if originalRun != nil {
|
||||
originalRun(cmd, args)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// addCommandWithAuth adds a command and all its subcommands with authentication check
|
||||
func addCommandWithAuth(parent *cobra.Command, cmd *cobra.Command) {
|
||||
// Apply auth check to the command itself
|
||||
withAuth(cmd)
|
||||
|
||||
// Apply auth check to all subcommands recursively
|
||||
for _, subCmd := range cmd.Commands() {
|
||||
withAuth(subCmd)
|
||||
}
|
||||
|
||||
parent.AddCommand(cmd)
|
||||
}
|
||||
|
||||
// initialize all the subcommands
|
||||
func initCommands() {
|
||||
cobra.EnableCommandSorting = false
|
||||
rootCmd.AddGroup(
|
||||
&cobra.Group{
|
||||
ID: "api",
|
||||
Title: "API Server Operations",
|
||||
},
|
||||
&cobra.Group{
|
||||
ID: "query",
|
||||
Title: "Query Operations",
|
||||
},
|
||||
)
|
||||
|
||||
// Add authentication commands (no auth required)
|
||||
rootCmd.AddCommand(apiserver.NewLogonCmd(configManager))
|
||||
rootCmd.AddCommand(apiserver.NewLogoutCmd(configManager))
|
||||
|
||||
// Add commands that require authentication
|
||||
// These commands will automatically check authentication before execution
|
||||
addCommandWithAuth(rootCmd, apiserver.NewListCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, permissions.NewPermsCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, roles.NewRolesCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, users.NewUsersCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, jobs.NewJobsCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, hosts.NewHostsCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, queues.NewQueuesCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, clusters.NewClustersCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, resources.NewResourcesCmd(configManager))
|
||||
rootCmd.SetFlagErrorFunc(request.FlagErrorHandler)
|
||||
}
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
apiserver "xcecli/cmd/auth"
|
||||
"xcecli/cmd/clusters"
|
||||
"xcecli/cmd/hosts"
|
||||
"xcecli/cmd/jobs"
|
||||
"xcecli/cmd/permissions"
|
||||
"xcecli/cmd/queues"
|
||||
"xcecli/cmd/resources"
|
||||
"xcecli/cmd/roles"
|
||||
"xcecli/cmd/users"
|
||||
"xcecli/common/request"
|
||||
"xcecli/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "xce",
|
||||
Short: "XCE CLI tool for the API Server operations",
|
||||
}
|
||||
configManager *config.ConfigManager
|
||||
)
|
||||
|
||||
// execute the root command
|
||||
func Execute(cm *config.ConfigManager, version string) error {
|
||||
if cm == nil {
|
||||
return fmt.Errorf("the config manager is not initialized")
|
||||
}
|
||||
configManager = cm
|
||||
rootCmd.Version = version
|
||||
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||
rootCmd.SilenceErrors = true
|
||||
rootCmd.SilenceUsage = true
|
||||
|
||||
// initialize the subcommands
|
||||
initCommands()
|
||||
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
// withAuth wraps a command with authentication check
|
||||
// This function creates a middleware that checks authentication before executing any command
|
||||
func withAuth(cmd *cobra.Command) *cobra.Command {
|
||||
// Store original handlers
|
||||
originalRunE := cmd.RunE
|
||||
originalRun := cmd.Run
|
||||
|
||||
// Create new RunE that checks authentication first
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
// Check authentication before executing the command
|
||||
_, err := configManager.IsAuthenticated()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute the original command logic
|
||||
if originalRunE != nil {
|
||||
return originalRunE(cmd, args)
|
||||
}
|
||||
if originalRun != nil {
|
||||
originalRun(cmd, args)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// addCommandWithAuth adds a command and all its subcommands with authentication check
|
||||
func addCommandWithAuth(parent *cobra.Command, cmd *cobra.Command) {
|
||||
// Apply auth check to the command itself
|
||||
withAuth(cmd)
|
||||
|
||||
// Apply auth check to all subcommands recursively
|
||||
for _, subCmd := range cmd.Commands() {
|
||||
withAuth(subCmd)
|
||||
}
|
||||
|
||||
parent.AddCommand(cmd)
|
||||
}
|
||||
|
||||
// initialize all the subcommands
|
||||
func initCommands() {
|
||||
cobra.EnableCommandSorting = false
|
||||
rootCmd.AddGroup(
|
||||
&cobra.Group{
|
||||
ID: "api",
|
||||
Title: "API Server Operations",
|
||||
},
|
||||
&cobra.Group{
|
||||
ID: "query",
|
||||
Title: "Query Operations",
|
||||
},
|
||||
)
|
||||
|
||||
// Add authentication commands (no auth required)
|
||||
rootCmd.AddCommand(apiserver.NewLogonCmd(configManager))
|
||||
rootCmd.AddCommand(apiserver.NewLogoutCmd(configManager))
|
||||
|
||||
// Add commands that require authentication
|
||||
// These commands will automatically check authentication before execution
|
||||
addCommandWithAuth(rootCmd, apiserver.NewListCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, permissions.NewPermsCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, roles.NewRolesCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, users.NewUsersCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, jobs.NewJobsCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, hosts.NewHostsCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, queues.NewQueuesCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, clusters.NewClustersCmd(configManager))
|
||||
addCommandWithAuth(rootCmd, resources.NewResourcesCmd(configManager))
|
||||
rootCmd.SetFlagErrorFunc(request.FlagErrorHandler)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue