diff --git a/management/server/account/manager.go b/management/server/account/manager.go index 5d2a9b037..13154b98c 100644 --- a/management/server/account/manager.go +++ b/management/server/account/manager.go @@ -123,7 +123,7 @@ type Manager interface { UpdateToPrimaryAccount(ctx context.Context, accountId string) error GetOwnerInfo(ctx context.Context, accountId string) (*types.UserInfo, error) GetCurrentUserInfo(ctx context.Context, userAuth nbcontext.UserAuth) (*users.UserInfoWithPermissions, error) - CreateJob(ctx context.Context, accountID, peerID, userID string, job *types.Job) error - GetAllJobs(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) - GetJobByID(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) + CreatePeerJob(ctx context.Context, accountID, peerID, userID string, job *types.Job) error + GetAllPeerJobs(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) + GetPeerJobByID(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) } diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index ab3b95e2e..70ef3b969 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -286,6 +286,8 @@ var activityMap = map[Activity]Code{ AccountNetworkRangeUpdated: {"Account network range updated", "account.network.range.update"}, PeerIPUpdated: {"Peer IP updated", "peer.ip.update"}, + + JobCreatedByUser: {"Create Job for peer", "peer.job.created"}, } // StringCode returns a string code of the activity diff --git a/management/server/http/handlers/peers/peers_handler.go b/management/server/http/handlers/peers/peers_handler.go index 5331434cc..ccd545546 100644 --- a/management/server/http/handlers/peers/peers_handler.go +++ b/management/server/http/handlers/peers/peers_handler.go @@ -58,17 +58,17 @@ func (h *Handler) CreateJob(w http.ResponseWriter, r *http.Request) { req := &api.JobRequest{} if err := json.NewDecoder(r.Body).Decode(req); err != nil { - util.WriteErrorResponse("invalid JSON payload", http.StatusBadRequest, w) + util.WriteError(ctx, err, w) return } job, err := types.NewJob(userAuth.UserId, userAuth.AccountId, peerID, types.JobType(req.Type), req.Parameters) if err != nil { - util.WriteErrorResponse(fmt.Sprintf("invalid Job request %v", err), http.StatusBadRequest, w) + util.WriteError(ctx, err, w) return } - if err := h.accountManager.CreateJob(ctx, userAuth.AccountId, peerID, userAuth.UserId, job); err != nil { - util.WriteErrorResponse(fmt.Sprintf("failed to create job %v", err), http.StatusInternalServerError, w) + if err := h.accountManager.CreatePeerJob(ctx, userAuth.AccountId, peerID, userAuth.UserId, job); err != nil { + util.WriteError(ctx, err, w) return } @@ -79,16 +79,16 @@ func (h *Handler) ListJobs(w http.ResponseWriter, r *http.Request) { ctx := r.Context() userAuth, err := nbcontext.GetUserAuthFromContext(ctx) if err != nil { - util.WriteError(r.Context(), err, w) + util.WriteError(ctx, err, w) return } vars := mux.Vars(r) peerID := vars["peerId"] - jobs, err := h.accountManager.GetAllJobs(ctx, userAuth.AccountId, userAuth.UserId, peerID) + jobs, err := h.accountManager.GetAllPeerJobs(ctx, userAuth.AccountId, userAuth.UserId, peerID) if err != nil { - util.WriteErrorResponse(fmt.Sprintf("failed to fetch jobs %v", err), http.StatusInternalServerError, w) + util.WriteError(ctx, err, w) return } @@ -107,9 +107,9 @@ func (h *Handler) GetJob(w http.ResponseWriter, r *http.Request) { peerID := vars["peerId"] jobID := vars["jobId"] - job, err := h.accountManager.GetJobByID(ctx, userAuth.AccountId, userAuth.UserId, peerID, jobID) + job, err := h.accountManager.GetPeerJobByID(ctx, userAuth.AccountId, userAuth.UserId, peerID, jobID) if err != nil { - util.WriteErrorResponse(fmt.Sprintf("failed to fetch job %v", err), http.StatusInternalServerError, w) + util.WriteError(ctx, err, w) return } diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 4b812a810..070a7ddd1 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -123,27 +123,27 @@ type MockAccountManager struct { GetOrCreateAccountByPrivateDomainFunc func(ctx context.Context, initiatorId, domain string) (*types.Account, bool, error) UpdateAccountPeersFunc func(ctx context.Context, accountID string) BufferUpdateAccountPeersFunc func(ctx context.Context, accountID string) - CreateJobFunc func(ctx context.Context, accountID, peerID, userID string, job *types.Job) error - GetAllJobsFunc func(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) - GetJobByIDFunc func(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) + CreatePeerJobFunc func(ctx context.Context, accountID, peerID, userID string, job *types.Job) error + GetAllPeerJobsFunc func(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) + GetPeerJobByIDFunc func(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) } -func (am *MockAccountManager) CreateJob(ctx context.Context, accountID, peerID, userID string, job *types.Job) error { - if am.CreateJobFunc != nil { - return am.CreateJobFunc(ctx, accountID, peerID, userID, job) +func (am *MockAccountManager) CreatePeerJob(ctx context.Context, accountID, peerID, userID string, job *types.Job) error { + if am.CreatePeerJobFunc != nil { + return am.CreatePeerJobFunc(ctx, accountID, peerID, userID, job) } return status.Errorf(codes.Unimplemented, "method CreateJob is not implemented") } -func (am *MockAccountManager) GetAllJobs(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) { - if am.CreateJobFunc != nil { - return am.GetAllJobsFunc(ctx, accountID, userID, peerID) +func (am *MockAccountManager) GetAllPeerJobs(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) { + if am.CreatePeerJobFunc != nil { + return am.GetAllPeerJobsFunc(ctx, accountID, userID, peerID) } return nil, status.Errorf(codes.Unimplemented, "method GetAllJobs is not implemented") } -func (am *MockAccountManager) GetJobByID(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) { - if am.CreateJobFunc != nil { - return am.GetJobByIDFunc(ctx, accountID, userID, peerID, jobID) +func (am *MockAccountManager) GetPeerJobByID(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) { + if am.CreatePeerJobFunc != nil { + return am.GetPeerJobByIDFunc(ctx, accountID, userID, peerID, jobID) } return nil, status.Errorf(codes.Unimplemented, "method CreateJob is not implemented") } diff --git a/management/server/peer.go b/management/server/peer.go index 63dab7ca7..dfe79ec6f 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -333,10 +333,7 @@ func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, user return peer, nil } -func (am *DefaultAccountManager) CreateJob(ctx context.Context, accountID, peerID, userID string, job *types.Job) error { - unlock := am.Store.AcquireWriteLockByUID(ctx, accountID) - defer unlock() - +func (am *DefaultAccountManager) CreatePeerJob(ctx context.Context, accountID, peerID, userID string, job *types.Job) error { // todo: Create permissions for job allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Delete) if err != nil { @@ -382,23 +379,20 @@ func (am *DefaultAccountManager) CreateJob(ctx context.Context, accountID, peerI if err != nil { return err } - if err := transaction.SaveJob(ctx, job); err != nil { + if err := transaction.CreatePeerJob(ctx, job); err != nil { return fmt.Errorf("failed to save job for peer %s: %w", peer.ID, err) } - - settings, err := transaction.GetAccountSettings(ctx, store.LockingStrengthNone, accountID) - - if err != nil { - return err + + jobMeta := map[string]any{ + "job_id": job.ID, + "for_peer_id": job.PeerID, + "job_type": job.Type, + "job_status": job.Status, + "job_parameters": job.Parameters, } - dnsDomain := am.GetDNSDomain(settings) eventsToStore = func() { - am.StoreEvent(ctx, userID, peer.ID, accountID, activity.JobCreatedByUser, peer.EventMeta(dnsDomain)) - } - - if err = transaction.IncrementNetworkSerial(ctx, accountID); err != nil { - return fmt.Errorf("failed to increment network serial: %w", err) + am.StoreEvent(ctx, userID, peer.ID, accountID, activity.JobCreatedByUser, jobMeta) } return nil }) @@ -411,7 +405,7 @@ func (am *DefaultAccountManager) CreateJob(ctx context.Context, accountID, peerI return nil } -func (am *DefaultAccountManager) GetAllJobs(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) { +func (am *DefaultAccountManager) GetAllPeerJobs(ctx context.Context, accountID, userID, peerID string) ([]*types.Job, error) { // todo: Create permissions for job allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Delete) if err != nil { @@ -430,7 +424,7 @@ func (am *DefaultAccountManager) GetAllJobs(ctx context.Context, accountID, user return []*types.Job{}, nil } - accountJobs, err := am.Store.GetJobs(ctx, accountID, peerID) + accountJobs, err := am.Store.GetPeerJobs(ctx, accountID, peerID) if err != nil { return nil, err } @@ -438,7 +432,7 @@ func (am *DefaultAccountManager) GetAllJobs(ctx context.Context, accountID, user return accountJobs, nil } -func (am *DefaultAccountManager) GetJobByID(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) { +func (am *DefaultAccountManager) GetPeerJobByID(ctx context.Context, accountID, userID, peerID, jobID string) (*types.Job, error) { // todo: Create permissions for job allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Delete) if err != nil { @@ -457,7 +451,7 @@ func (am *DefaultAccountManager) GetJobByID(ctx context.Context, accountID, user return &types.Job{}, nil } - job, err := am.Store.GetJobByID(ctx, accountID, jobID) + job, err := am.Store.GetPeerJobByID(ctx, accountID, jobID) if err != nil { return nil, err } diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 181c212d3..59c7d7b0b 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -127,24 +127,20 @@ func GetKeyQueryCondition(s *SqlStore) string { } // SaveJob persists a job in DB -func (s *SqlStore) SaveJob(ctx context.Context, job *types.Job) error { - start := time.Now() - defer func() { - if s.metrics != nil { - s.metrics.StoreMetrics().CountPersistenceDuration(time.Since(start)) - } - }() - - return s.db.WithContext(ctx). - Clauses(clause.OnConflict{UpdateAll: true}). - Create(job).Error +func (s *SqlStore) CreatePeerJob(ctx context.Context, job *types.Job) error { + result := s.db.Create(job) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to create job in store: %s", result.Error) + return status.Errorf(status.Internal, "failed to create job in store") + } + return nil } // job was pending for too long and has been cancelled // todo call it when we first start the jobChannel to make sure no stuck jobs func (s *SqlStore) MarkPendingJobsAsFailed(ctx context.Context, peerID string) error { now := time.Now().UTC() - return s.db.WithContext(ctx). + return s.db. Model(&types.Job{}). Where("peer_id = ? AND status = ?", types.JobStatusPending, peerID). Updates(map[string]any{ @@ -155,9 +151,9 @@ func (s *SqlStore) MarkPendingJobsAsFailed(ctx context.Context, peerID string) e } // GetJobByID fetches job by ID -func (s *SqlStore) GetJobByID(ctx context.Context, accountID, jobID string) (*types.Job, error) { +func (s *SqlStore) GetPeerJobByID(ctx context.Context, accountID, jobID string) (*types.Job, error) { var job types.Job - err := s.db.WithContext(ctx). + err := s.db. Where(accountAndIDQueryCondition, accountID, jobID). First(&job).Error if errors.Is(err, gorm.ErrRecordNotFound) { @@ -167,12 +163,13 @@ func (s *SqlStore) GetJobByID(ctx context.Context, accountID, jobID string) (*ty } // get all jobs -func (s *SqlStore) GetJobs(ctx context.Context, accountID, peerID string) ([]*types.Job, error) { +func (s *SqlStore) GetPeerJobs(ctx context.Context, accountID, peerID string) ([]*types.Job, error) { var jobs []*types.Job - err := s.db.WithContext(ctx). + err := s.db. Where(accountAndPeerIDQueryCondition, accountID, peerID). Order("created_at DESC"). Find(&jobs).Error + if err != nil { return nil, err } @@ -180,19 +177,26 @@ func (s *SqlStore) GetJobs(ctx context.Context, accountID, peerID string) ([]*ty return jobs, nil } -func (s *SqlStore) CompleteJob(ctx context.Context, accountID, jobID, result string, failedReason string) error { - job, err := s.GetJobByID(ctx, accountID, jobID) - if err != nil { - return err - } - // mark it as succeeded or failed - if result != "" && failedReason == "" { - job.MarkSucceeded(result) - } else { - job.MarkFailed(failedReason) +func (s *SqlStore) CompletePeerJob(accountID, jobID, result, failedReason string) error { + now := time.Now().UTC() + + updates := map[string]any{ + "completed_at": now, } - return s.db.WithContext(ctx).Save(job).Error + if result != "" && failedReason == "" { + updates["status"] = types.JobStatusSucceeded + updates["result"] = result + updates["failed_reason"] = "" + } else { + updates["status"] = types.JobStatusFailed + updates["failed_reason"] = failedReason + } + + return s.db. + Model(&types.Job{}). + Where(accountAndIDQueryCondition, accountID, jobID). + Updates(updates).Error } // AcquireGlobalLock acquires global lock across all the accounts and returns a function that releases the lock diff --git a/management/server/store/store.go b/management/server/store/store.go index 38e7d7c6f..98b9ae865 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -205,10 +205,10 @@ type Store interface { IsPrimaryAccount(ctx context.Context, accountID string) (bool, string, error) MarkAccountPrimary(ctx context.Context, accountID string) error UpdateAccountNetwork(ctx context.Context, accountID string, ipNet net.IPNet) error - SaveJob(ctx context.Context, job *types.Job) error - GetJobByID(ctx context.Context, accountID, jobID string) (*types.Job, error) - GetJobs(ctx context.Context, accountID, peerID string) ([]*types.Job, error) - CompleteJob(ctx context.Context, accountID, jobID, result string, failedReason string) error + CreatePeerJob(ctx context.Context, job *types.Job) error + CompletePeerJob(accountID, jobID, result, failedReason string) error + GetPeerJobByID(ctx context.Context, accountID, jobID string) (*types.Job, error) + GetPeerJobs(ctx context.Context, accountID, peerID string) ([]*types.Job, error) MarkPendingJobsAsFailed(ctx context.Context, peerID string) error } diff --git a/management/server/types/job.go b/management/server/types/job.go index aa0473202..4f5950b92 100644 --- a/management/server/types/job.go +++ b/management/server/types/job.go @@ -36,7 +36,7 @@ type Job struct { // TriggeredBy user that triggered this job TriggeredBy string `gorm:"index"` - PeerID string `gorm:"index"` + PeerID string `gorm:"index"` AccountID string `gorm:"index"` @@ -110,52 +110,19 @@ func (j *Job) encodeParameters(params map[string]any) error { return nil } - -// MarkSucceeded sets job as completed successfully -func (j *Job) MarkSucceeded(result string) { - now := time.Now().UTC() - j.Status = JobStatusSucceeded - j.Result = result - j.CompletedAt = &now -} - -// MarkFailed sets job as failed with reason -func (j *Job) MarkFailed(reason string) { - now := time.Now().UTC() - j.Status = JobStatusFailed - j.FailedReason = reason - j.CompletedAt = &now -} - - func (j *Job) validateJobRequest() error { if j == nil { return fmt.Errorf("job cannot be nil") } - if j.Type == "" { - return fmt.Errorf("job type must be specified") - } - if len(j.Parameters) == 0 { return fmt.Errorf("job parameters must be provided") } switch j.Type { case JobTypeBundle: - var params JobParametersBundle - if err := json.Unmarshal(j.Parameters, ¶ms); err != nil { - return fmt.Errorf("invalid parameters for bundle job: %w", err) - } - - // validate bundle_for_time <= 5 minutes - if params.BundleForTime < 0 || params.BundleForTime > 5 { - return fmt.Errorf("bundle_for_time must be between 0 and 5, got %d", params.BundleForTime) - } - - // validate log-file-count ≥ 1 and ≤ 1000 - if params.LogFileCount < 1 || params.LogFileCount > 1000 { - return fmt.Errorf("log-file-count must be between 1 and 1000, got %d", params.LogFileCount) + if err := j.validateDebugBundleJobParams(); err != nil { + return err } default: @@ -164,3 +131,20 @@ func (j *Job) validateJobRequest() error { return nil } + +func (j *Job) validateDebugBundleJobParams() error { + var params JobParametersBundle + if err := j.DecodeParameters(¶ms); err != nil { + return fmt.Errorf("invalid parameters for bundle job: %w", err) + } + // validate bundle_for_time <= 5 minutes + if params.BundleForTime < 0 || params.BundleForTime > 5 { + return fmt.Errorf("bundle_for_time must be between 0 and 5, got %d", params.BundleForTime) + } + + // validate log-file-count ≥ 1 and ≤ 1000 + if params.LogFileCount < 1 || params.LogFileCount > 1000 { + return fmt.Errorf("log-file-count must be between 1 and 1000, got %d", params.LogFileCount) + } + return nil +} diff --git a/shared/management/http/api/openapi.yml b/shared/management/http/api/openapi.yml index c943ead48..3c9d5c8c5 100644 --- a/shared/management/http/api/openapi.yml +++ b/shared/management/http/api/openapi.yml @@ -34,6 +34,86 @@ tags: x-cloud-only: true components: schemas: + JobRequest: + type: object + properties: + type: + type: string + description: The type of job to execute + example: bundle + enum: [ "bundle" ] + parameters: + type: object + description: Key-value parameters required for the job + additionalProperties: true + example: + bundle_for: true + bundle_for_time: 5 + log_file_count: 2 + anonymize: false + required: + - type + - parameters + Job: + type: object + properties: + id: + type: string + description: Primary identifier + example: "123456" + createdAt: + type: string + format: date-time + description: When the job was created (UTC) + completedAt: + type: string + format: date-time + description: When the job finished, null if still running + triggeredBy: + type: string + description: User that triggered this job + example: "user_42" + peerId: + type: string + description: Associated peer ID + example: "peer_99" + accountId: + type: string + description: Associated account ID + example: "acc_77" + type: + type: string + enum: [ bundle ] + example: bundle + status: + type: string + enum: [ pending, succeeded, failed ] + example: pending + failedReason: + type: string + description: Why the job failed (if failed) + example: "Connection timeout" + result: + type: string + description: Job output (JSON, URL, etc.) + example: "https://example.com/bundle.zip" + parameters: + type: object + additionalProperties: true + description: Job configuration parameters + example: + bundle_for: true + bundle_for_time: 60 + log_file_count: 10 + anonymize: false + required: + - id + - createdAt + - triggeredBy + - peerId + - accountId + - type + - status Account: type: object properties: @@ -2170,6 +2250,108 @@ security: - BearerAuth: [ ] - TokenAuth: [ ] paths: + /api/peers/{peerId}/jobs: + get: + summary: List Jobs + description: Retrieve all jobs for a given peer + tags: [ Jobs ] + security: + - BearerAuth: [] + - TokenAuth: [] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + responses: + '200': + description: List of jobs + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Job' + '400': + $ref: '#/components/responses/bad_request' + '401': + $ref: '#/components/responses/requires_authentication' + '403': + $ref: '#/components/responses/forbidden' + '500': + $ref: '#/components/responses/internal_error' + post: + summary: Create Job + description: Create a new job for a given peer + tags: [ Jobs ] + security: + - BearerAuth: [] + - TokenAuth: [] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + requestBody: + description: Create job request + content: + application/json: + schema: + $ref: '#/components/schemas/JobRequest' + required: true + responses: + '201': + description: Job created + content: + application/json: + schema: + $ref: '#/components/schemas/Job' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/peers/{peerId}/jobs/{jobId}: + get: + summary: Get Job + description: Retrieve details of a specific job + tags: [ Jobs ] + security: + - BearerAuth: [] + - TokenAuth: [] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + - in: path + name: jobId + required: true + schema: + type: string + responses: + '200': + description: A Job object + content: + application/json: + schema: + $ref: '#/components/schemas/Job' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" /api/accounts: get: summary: List all Accounts diff --git a/shared/management/http/api/types.gen.go b/shared/management/http/api/types.gen.go index 8e4d92163..ba29847b9 100644 --- a/shared/management/http/api/types.gen.go +++ b/shared/management/http/api/types.gen.go @@ -104,6 +104,23 @@ const ( IngressPortAllocationRequestPortRangeProtocolUdp IngressPortAllocationRequestPortRangeProtocol = "udp" ) +// Defines values for JobStatus. +const ( + JobStatusFailed JobStatus = "failed" + JobStatusPending JobStatus = "pending" + JobStatusSucceeded JobStatus = "succeeded" +) + +// Defines values for JobType. +const ( + JobTypeBundle JobType = "bundle" +) + +// Defines values for JobRequestType. +const ( + JobRequestTypeBundle JobRequestType = "bundle" +) + // Defines values for NameserverNsType. const ( NameserverNsTypeUdp NameserverNsType = "udp" @@ -199,11 +216,6 @@ const ( GetApiEventsNetworkTrafficParamsDirectionINGRESS GetApiEventsNetworkTrafficParamsDirection = "INGRESS" ) -type JobRequest struct { - Type string `json:"type" binding:"required"` // Job type, e.g., "bundle" - Parameters map[string]any `json:"parameters" binding:"required"` // Dynamic parameters -} - // AccessiblePeer defines model for AccessiblePeer. type AccessiblePeer struct { // CityName Commonly used English name of the city @@ -648,6 +660,56 @@ type IngressPortAllocationRequestPortRange struct { // IngressPortAllocationRequestPortRangeProtocol The protocol accepted by the port range type IngressPortAllocationRequestPortRangeProtocol string +// Job defines model for Job. +type Job struct { + // AccountId Associated account ID + AccountId string `json:"accountId"` + + // CompletedAt When the job finished, null if still running + CompletedAt *time.Time `json:"completedAt,omitempty"` + + // CreatedAt When the job was created (UTC) + CreatedAt time.Time `json:"createdAt"` + + // FailedReason Why the job failed (if failed) + FailedReason *string `json:"failedReason,omitempty"` + + // Id Primary identifier + Id string `json:"id"` + + // Parameters Job configuration parameters + Parameters *map[string]interface{} `json:"parameters,omitempty"` + + // PeerId Associated peer ID + PeerId string `json:"peerId"` + + // Result Job output (JSON, URL, etc.) + Result *string `json:"result,omitempty"` + Status JobStatus `json:"status"` + + // TriggeredBy User that triggered this job + TriggeredBy string `json:"triggeredBy"` + Type JobType `json:"type"` +} + +// JobStatus defines model for Job.Status. +type JobStatus string + +// JobType defines model for Job.Type. +type JobType string + +// JobRequest defines model for JobRequest. +type JobRequest struct { + // Parameters Key-value parameters required for the job + Parameters map[string]interface{} `json:"parameters"` + + // Type The type of job to execute + Type JobRequestType `json:"type"` +} + +// JobRequestType The type of job to execute +type JobRequestType string + // Location Describe geographical location information type Location struct { // CityName Commonly used English name of the city @@ -1020,8 +1082,6 @@ type OSVersionCheck struct { // Peer defines model for Peer. type Peer struct { - // CreatedAt Peer creation date (UTC) - CreatedAt time.Time `json:"created_at"` // ApprovalRequired (Cloud only) Indicates whether peer needs approval ApprovalRequired bool `json:"approval_required"` @@ -1037,6 +1097,9 @@ type Peer struct { // CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country CountryCode CountryCode `json:"country_code"` + // CreatedAt Peer creation date (UTC) + CreatedAt time.Time `json:"created_at"` + // DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud DnsLabel string `json:"dns_label"` @@ -1103,8 +1166,6 @@ type Peer struct { // PeerBatch defines model for PeerBatch. type PeerBatch struct { - // CreatedAt Peer creation date (UTC) - CreatedAt time.Time `json:"created_at"` // AccessiblePeersCount Number of accessible peers AccessiblePeersCount int `json:"accessible_peers_count"` @@ -1123,6 +1184,9 @@ type PeerBatch struct { // CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country CountryCode CountryCode `json:"country_code"` + // CreatedAt Peer creation date (UTC) + CreatedAt time.Time `json:"created_at"` + // DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud DnsLabel string `json:"dns_label"` @@ -1940,6 +2004,9 @@ type PostApiPeersPeerIdIngressPortsJSONRequestBody = IngressPortAllocationReques // PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody defines body for PutApiPeersPeerIdIngressPortsAllocationId for application/json ContentType. type PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody = IngressPortAllocationRequest +// PostApiPeersPeerIdJobsJSONRequestBody defines body for PostApiPeersPeerIdJobs for application/json ContentType. +type PostApiPeersPeerIdJobsJSONRequestBody = JobRequest + // PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType. type PostApiPoliciesJSONRequestBody = PolicyUpdate