feat: 完整游戏运维功能
This commit is contained in:
+247
-42
@@ -31,6 +31,44 @@ type User struct {
|
||||
|
||||
func (User) TableName() string { return "users" }
|
||||
|
||||
type AuthSession struct {
|
||||
// ID is a non-secret stable session record identifier.
|
||||
ID string `json:"id" db:"id"`
|
||||
// UserID owns the authenticated session.
|
||||
UserID string `json:"userId" db:"user_id"`
|
||||
// TokenHash is a one-way verifier; the bearer token is never persisted.
|
||||
TokenHash string `json:"tokenHash" db:"token_hash"`
|
||||
// Status tracks active or revoked lifecycle state.
|
||||
Status domain.AuthSessionStatus `json:"status" db:"status"`
|
||||
// Generation increments when a user rotates a session.
|
||||
Generation int `json:"generation" db:"generation"`
|
||||
IssuedAt time.Time `json:"issuedAt" db:"issued_at"`
|
||||
ExpiresAt time.Time `json:"expiresAt" db:"expires_at"`
|
||||
LastSeenAt time.Time `json:"lastSeenAt" db:"last_seen_at"`
|
||||
RevokedAt time.Time `json:"revokedAt,omitempty" db:"revoked_at"`
|
||||
}
|
||||
|
||||
func (AuthSession) TableName() string { return "auth_sessions" }
|
||||
|
||||
type RunControlSession struct {
|
||||
// RunEndpointID is both the endpoint owner and stable session record ID.
|
||||
RunEndpointID string `json:"runEndpointId" db:"run_endpoint_id"`
|
||||
// SessionTokenHash is a one-way verifier; raw Run tokens are never persisted.
|
||||
SessionTokenHash string `json:"sessionTokenHash" db:"session_token_hash"`
|
||||
Status domain.AuthSessionStatus `json:"status" db:"status"`
|
||||
Generation int `json:"generation" db:"generation"`
|
||||
CapabilityFingerprint string `json:"capabilityFingerprint" db:"capability_fingerprint"`
|
||||
HeartbeatIntervalSeconds int `json:"heartbeatIntervalSeconds" db:"heartbeat_interval_seconds"`
|
||||
CreatedAt time.Time `json:"createdAt" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
|
||||
ExpiresAt time.Time `json:"expiresAt" db:"expires_at"`
|
||||
RevokedAt time.Time `json:"revokedAt,omitempty" db:"revoked_at"`
|
||||
RequireSignedRequests bool `json:"requireSignedRequests" db:"require_signed_requests"`
|
||||
UsedNonces []string `json:"usedNonces,omitempty" db:"used_nonces"`
|
||||
}
|
||||
|
||||
func (RunControlSession) TableName() string { return "run_control_sessions" }
|
||||
|
||||
type AIProvider struct {
|
||||
// ID is the stable AI provider identifier.
|
||||
ID string `json:"id" db:"id"`
|
||||
@@ -145,6 +183,8 @@ type GamePlugin struct {
|
||||
AIPurposes []string `json:"aiPurposes" db:"ai_purposes"`
|
||||
// RemoteAccess stores plugin-declared remote access metadata.
|
||||
RemoteAccess GamePluginRemoteAccess `json:"remoteAccess" db:"remote_access"`
|
||||
// RuntimeProfiles stores validated manifest-declared runtime contracts.
|
||||
RuntimeProfiles domain.GamePluginRuntimeProfiles `json:"runtimeProfiles" db:"runtime_profiles"`
|
||||
// ValidationViolations stores safe validation findings for invalid plugins.
|
||||
ValidationViolations []string `json:"validationViolations" db:"validation_violations"`
|
||||
// Status is the plugin lifecycle status.
|
||||
@@ -172,6 +212,14 @@ type ServerInstance struct {
|
||||
State domain.ServerInstanceState `json:"state" db:"state"`
|
||||
// ConfigVersion is the platform-managed optimistic concurrency version.
|
||||
ConfigVersion int `json:"configVersion" db:"config_version"`
|
||||
// ConfigKey is the logical configuration target, never a host path.
|
||||
ConfigKey string `json:"configKey,omitempty" db:"config_key"`
|
||||
// ConfigContent is the last platform-approved bounded configuration body.
|
||||
ConfigContent string `json:"configContent,omitempty" db:"config_content"`
|
||||
// ConfigChecksum is the SHA-256 checksum of ConfigContent.
|
||||
ConfigChecksum string `json:"configChecksum,omitempty" db:"config_checksum"`
|
||||
// ConfigUpdatedAt records the last accepted config execution result.
|
||||
ConfigUpdatedAt time.Time `json:"configUpdatedAt,omitempty" db:"config_updated_at"`
|
||||
// CreatedAt is the record creation timestamp.
|
||||
CreatedAt time.Time `json:"createdAt" db:"created_at"`
|
||||
// UpdatedAt is the last update timestamp.
|
||||
@@ -198,6 +246,10 @@ type RunEndpoint struct {
|
||||
DisplayName string `json:"displayName" db:"display_name"`
|
||||
// Version is the run binary version.
|
||||
Version string `json:"version" db:"version"`
|
||||
// Platform is the endpoint operating system.
|
||||
Platform string `json:"platform" db:"platform"`
|
||||
// Architecture is the endpoint CPU architecture.
|
||||
Architecture string `json:"architecture" db:"architecture"`
|
||||
// Status is the current endpoint status.
|
||||
Status domain.RunEndpointStatus `json:"status" db:"status"`
|
||||
// Capabilities lists advertised run capability keys.
|
||||
@@ -217,6 +269,38 @@ type JobProgress struct {
|
||||
Message string `json:"message,omitempty" db:"message"`
|
||||
}
|
||||
|
||||
type JobRetryPolicy struct {
|
||||
// MaxAttempts bounds total claims, including the first attempt.
|
||||
MaxAttempts int `json:"maxAttempts" db:"max_attempts"`
|
||||
// InitialBackoffSeconds is the first retry delay.
|
||||
InitialBackoffSeconds int `json:"initialBackoffSeconds" db:"initial_backoff_seconds"`
|
||||
// MaxBackoffSeconds caps exponential retry delay.
|
||||
MaxBackoffSeconds int `json:"maxBackoffSeconds" db:"max_backoff_seconds"`
|
||||
}
|
||||
|
||||
type JobExecutionInput struct {
|
||||
WorkspaceScope string `json:"workspaceScope,omitempty" db:"workspace_scope"`
|
||||
Content string `json:"content,omitempty" db:"content"`
|
||||
ExpectedVersion int `json:"expectedVersion,omitempty" db:"expected_version"`
|
||||
ExpectedChecksum string `json:"expectedChecksum,omitempty" db:"expected_checksum"`
|
||||
MaxReadBytes int `json:"maxReadBytes,omitempty" db:"max_read_bytes"`
|
||||
RemoteAdapterKey string `json:"remoteAdapterKey,omitempty" db:"remote_adapter_key"`
|
||||
RemoteAdapterKind string `json:"remoteAdapterKind,omitempty" db:"remote_adapter_kind"`
|
||||
TimeoutSeconds int `json:"timeoutSeconds,omitempty" db:"timeout_seconds"`
|
||||
}
|
||||
|
||||
type JobExecutionResult struct {
|
||||
Kind string `json:"kind,omitempty" db:"kind"`
|
||||
ProcessState string `json:"processState,omitempty" db:"process_state"`
|
||||
ExitClassification string `json:"exitClassification,omitempty" db:"exit_classification"`
|
||||
ExitCode int `json:"exitCode,omitempty" db:"exit_code"`
|
||||
Version int `json:"version,omitempty" db:"version"`
|
||||
Checksum string `json:"checksum,omitempty" db:"checksum"`
|
||||
SizeBytes int64 `json:"sizeBytes,omitempty" db:"size_bytes"`
|
||||
AuditSummary string `json:"auditSummary,omitempty" db:"audit_summary"`
|
||||
Content string `json:"content,omitempty" db:"content"`
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
// ID is the stable job identifier.
|
||||
ID string `json:"id" db:"id"`
|
||||
@@ -238,6 +322,39 @@ type Job struct {
|
||||
Progress JobProgress `json:"progress" db:"progress"`
|
||||
// ResultRef references the terminal result artifact or summary.
|
||||
ResultRef string `json:"resultRef,omitempty" db:"result_ref"`
|
||||
// ExecutionInput is private approved input delivered only to fenced Run assignments.
|
||||
ExecutionInput JobExecutionInput `json:"executionInput,omitempty" db:"execution_input"`
|
||||
// ExecutionResult stores typed execution evidence; private content is not user-projected.
|
||||
ExecutionResult JobExecutionResult `json:"executionResult,omitempty" db:"execution_result"`
|
||||
// RetryPolicy stores bounded durable retry settings.
|
||||
RetryPolicy JobRetryPolicy `json:"retryPolicy" db:"retry_policy"`
|
||||
// Attempt is the current monotonic per-job attempt.
|
||||
Attempt int `json:"attempt" db:"attempt"`
|
||||
// QueueEligibleAt is the first time queued work may be claimed.
|
||||
QueueEligibleAt time.Time `json:"queueEligibleAt,omitempty" db:"queue_eligible_at"`
|
||||
// NextAttemptAt is the durable retry eligibility time.
|
||||
NextAttemptAt time.Time `json:"nextAttemptAt,omitempty" db:"next_attempt_at"`
|
||||
// LeaseTokenHash stores a one-way verifier, never the raw lease token.
|
||||
LeaseTokenHash string `json:"leaseTokenHash,omitempty" db:"lease_token_hash"`
|
||||
// LeaseSessionGen fences the attempt to an authenticated Run session generation.
|
||||
LeaseSessionGen int `json:"leaseSessionGeneration,omitempty" db:"lease_session_generation"`
|
||||
// AckDeadlineAt bounds how long Run has to acknowledge a claim.
|
||||
AckDeadlineAt time.Time `json:"ackDeadlineAt,omitempty" db:"ack_deadline_at"`
|
||||
// LeaseExpiresAt bounds execution without progress or reconciliation.
|
||||
LeaseExpiresAt time.Time `json:"leaseExpiresAt,omitempty" db:"lease_expires_at"`
|
||||
// LastProgressSeq rejects reordered progress updates.
|
||||
LastProgressSeq uint64 `json:"lastProgressSequence,omitempty" db:"last_progress_sequence"`
|
||||
// CancelReason and timestamps persist cancellation intent and result.
|
||||
CancelReason string `json:"cancelReason,omitempty" db:"cancel_reason"`
|
||||
CancelRequestedAt time.Time `json:"cancelRequestedAt,omitempty" db:"cancel_requested_at"`
|
||||
CancelCompletedAt time.Time `json:"cancelCompletedAt,omitempty" db:"cancel_completed_at"`
|
||||
// TerminalAt and TerminalFingerprint make terminal replay durable and idempotent.
|
||||
TerminalAt time.Time `json:"terminalAt,omitempty" db:"terminal_at"`
|
||||
TerminalFingerprint string `json:"terminalFingerprint,omitempty" db:"terminal_fingerprint"`
|
||||
// Reconciliation fields provide restart recovery evidence.
|
||||
LastReconciledAt time.Time `json:"lastReconciledAt,omitempty" db:"last_reconciled_at"`
|
||||
ReconcileCount int `json:"reconcileCount,omitempty" db:"reconcile_count"`
|
||||
ReconcileOutcome string `json:"reconcileOutcome,omitempty" db:"reconcile_outcome"`
|
||||
// CreatedAt is the record creation timestamp.
|
||||
CreatedAt time.Time `json:"createdAt" db:"created_at"`
|
||||
// UpdatedAt is the last update timestamp.
|
||||
@@ -395,6 +512,7 @@ func GamePluginFromDomain(plugin domain.GamePlugin) GamePlugin {
|
||||
Tags: plugin.Tags,
|
||||
AIPurposes: plugin.AIPurposes,
|
||||
RemoteAccess: remoteAccessFromDomain(plugin.RemoteAccess),
|
||||
RuntimeProfiles: domain.CopyGamePluginRuntimeProfiles(plugin.RuntimeProfiles),
|
||||
ValidationViolations: plugin.ValidationViolations,
|
||||
Status: plugin.Status,
|
||||
}
|
||||
@@ -419,6 +537,7 @@ func (plugin GamePlugin) ToDomain() domain.GamePlugin {
|
||||
Tags: domain.CopyStringSlice(plugin.Tags),
|
||||
AIPurposes: domain.CopyStringSlice(plugin.AIPurposes),
|
||||
RemoteAccess: plugin.RemoteAccess.ToDomain(),
|
||||
RuntimeProfiles: domain.CopyGamePluginRuntimeProfiles(plugin.RuntimeProfiles),
|
||||
ValidationViolations: domain.CopyStringSlice(plugin.ValidationViolations),
|
||||
Status: plugin.Status,
|
||||
}
|
||||
@@ -521,29 +640,41 @@ func remoteAccessFromDomain(remote domain.GamePluginRemoteAccess) GamePluginRemo
|
||||
|
||||
func ServerInstanceFromDomain(instance domain.ServerInstance) ServerInstance {
|
||||
return ServerInstance{
|
||||
ID: instance.ID,
|
||||
PluginID: instance.PluginID,
|
||||
PluginVersion: instance.PluginVersion,
|
||||
RunEndpointID: instance.RunEndpointID,
|
||||
Name: instance.Name,
|
||||
State: instance.State,
|
||||
ConfigVersion: instance.ConfigVersion,
|
||||
CreatedAt: instance.CreatedAt,
|
||||
UpdatedAt: instance.UpdatedAt,
|
||||
ID: instance.ID,
|
||||
PluginID: instance.PluginID,
|
||||
PluginVersion: instance.PluginVersion,
|
||||
RunEndpointID: instance.RunEndpointID,
|
||||
Name: instance.Name,
|
||||
OwnerUserID: instance.OwnerUserID,
|
||||
AdminUserIDs: domain.CopyStringSlice(instance.AdminUserIDs),
|
||||
State: instance.State,
|
||||
ConfigVersion: instance.ConfigVersion,
|
||||
ConfigKey: instance.ConfigKey,
|
||||
ConfigContent: instance.ConfigContent,
|
||||
ConfigChecksum: instance.ConfigChecksum,
|
||||
ConfigUpdatedAt: instance.ConfigUpdatedAt,
|
||||
CreatedAt: instance.CreatedAt,
|
||||
UpdatedAt: instance.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func (instance ServerInstance) ToDomain() domain.ServerInstance {
|
||||
return domain.ServerInstance{
|
||||
ID: instance.ID,
|
||||
PluginID: instance.PluginID,
|
||||
PluginVersion: instance.PluginVersion,
|
||||
RunEndpointID: instance.RunEndpointID,
|
||||
Name: instance.Name,
|
||||
State: instance.State,
|
||||
ConfigVersion: instance.ConfigVersion,
|
||||
CreatedAt: instance.CreatedAt,
|
||||
UpdatedAt: instance.UpdatedAt,
|
||||
ID: instance.ID,
|
||||
PluginID: instance.PluginID,
|
||||
PluginVersion: instance.PluginVersion,
|
||||
RunEndpointID: instance.RunEndpointID,
|
||||
Name: instance.Name,
|
||||
OwnerUserID: instance.OwnerUserID,
|
||||
AdminUserIDs: domain.CopyStringSlice(instance.AdminUserIDs),
|
||||
State: instance.State,
|
||||
ConfigVersion: instance.ConfigVersion,
|
||||
ConfigKey: instance.ConfigKey,
|
||||
ConfigContent: instance.ConfigContent,
|
||||
ConfigChecksum: instance.ConfigChecksum,
|
||||
ConfigUpdatedAt: instance.ConfigUpdatedAt,
|
||||
CreatedAt: instance.CreatedAt,
|
||||
UpdatedAt: instance.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,6 +684,8 @@ func RunEndpointFromDomain(endpoint domain.RunEndpoint) RunEndpoint {
|
||||
ID: endpoint.ID,
|
||||
DisplayName: endpoint.DisplayName,
|
||||
Version: endpoint.Version,
|
||||
Platform: endpoint.Platform,
|
||||
Architecture: endpoint.Architecture,
|
||||
Status: endpoint.Status,
|
||||
Capabilities: endpoint.Capabilities,
|
||||
Capacity: capacityFromDomain(endpoint.Capacity),
|
||||
@@ -565,6 +698,8 @@ func (endpoint RunEndpoint) ToDomain() domain.RunEndpoint {
|
||||
ID: endpoint.ID,
|
||||
DisplayName: endpoint.DisplayName,
|
||||
Version: endpoint.Version,
|
||||
Platform: endpoint.Platform,
|
||||
Architecture: endpoint.Architecture,
|
||||
Status: endpoint.Status,
|
||||
Capabilities: domain.CopyStringSlice(endpoint.Capabilities),
|
||||
Capacity: endpoint.Capacity.ToDomain(),
|
||||
@@ -592,35 +727,105 @@ func capacityFromDomain(capacity domain.RunCapacity) RunCapacity {
|
||||
|
||||
func JobFromDomain(job domain.Job) Job {
|
||||
return Job{
|
||||
ID: job.ID,
|
||||
ServerInstanceID: job.ServerInstanceID,
|
||||
RunEndpointID: job.RunEndpointID,
|
||||
Capability: job.Capability,
|
||||
TargetKey: job.TargetKey,
|
||||
InputRef: job.InputRef,
|
||||
IdempotencyKey: job.IdempotencyKey,
|
||||
State: job.State,
|
||||
Progress: progressFromDomain(job.Progress),
|
||||
ResultRef: job.ResultRef,
|
||||
CreatedAt: job.CreatedAt,
|
||||
UpdatedAt: job.UpdatedAt,
|
||||
ID: job.ID,
|
||||
ServerInstanceID: job.ServerInstanceID,
|
||||
RunEndpointID: job.RunEndpointID,
|
||||
Capability: job.Capability,
|
||||
TargetKey: job.TargetKey,
|
||||
InputRef: job.InputRef,
|
||||
IdempotencyKey: job.IdempotencyKey,
|
||||
State: job.State,
|
||||
Progress: progressFromDomain(job.Progress),
|
||||
ResultRef: job.ResultRef,
|
||||
ExecutionInput: executionInputFromDomain(job.ExecutionInput),
|
||||
ExecutionResult: executionResultFromDomain(job.ExecutionResult),
|
||||
RetryPolicy: retryPolicyFromDomain(job.RetryPolicy),
|
||||
Attempt: job.Attempt,
|
||||
QueueEligibleAt: job.QueueEligibleAt,
|
||||
NextAttemptAt: job.NextAttemptAt,
|
||||
LeaseTokenHash: job.LeaseTokenHash,
|
||||
LeaseSessionGen: job.LeaseSessionGen,
|
||||
AckDeadlineAt: job.AckDeadlineAt,
|
||||
LeaseExpiresAt: job.LeaseExpiresAt,
|
||||
LastProgressSeq: job.LastProgressSeq,
|
||||
CancelReason: job.CancelReason,
|
||||
CancelRequestedAt: job.CancelRequestedAt,
|
||||
CancelCompletedAt: job.CancelCompletedAt,
|
||||
TerminalAt: job.TerminalAt,
|
||||
TerminalFingerprint: job.TerminalFingerprint,
|
||||
LastReconciledAt: job.LastReconciledAt,
|
||||
ReconcileCount: job.ReconcileCount,
|
||||
ReconcileOutcome: job.ReconcileOutcome,
|
||||
CreatedAt: job.CreatedAt,
|
||||
UpdatedAt: job.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func (job Job) ToDomain() domain.Job {
|
||||
return domain.Job{
|
||||
ID: job.ID,
|
||||
ServerInstanceID: job.ServerInstanceID,
|
||||
RunEndpointID: job.RunEndpointID,
|
||||
Capability: job.Capability,
|
||||
TargetKey: job.TargetKey,
|
||||
InputRef: job.InputRef,
|
||||
IdempotencyKey: job.IdempotencyKey,
|
||||
State: job.State,
|
||||
Progress: job.Progress.ToDomain(),
|
||||
ResultRef: job.ResultRef,
|
||||
CreatedAt: job.CreatedAt,
|
||||
UpdatedAt: job.UpdatedAt,
|
||||
ID: job.ID,
|
||||
ServerInstanceID: job.ServerInstanceID,
|
||||
RunEndpointID: job.RunEndpointID,
|
||||
Capability: job.Capability,
|
||||
TargetKey: job.TargetKey,
|
||||
InputRef: job.InputRef,
|
||||
IdempotencyKey: job.IdempotencyKey,
|
||||
State: job.State,
|
||||
Progress: job.Progress.ToDomain(),
|
||||
ResultRef: job.ResultRef,
|
||||
ExecutionInput: job.ExecutionInput.ToDomain(),
|
||||
ExecutionResult: job.ExecutionResult.ToDomain(),
|
||||
RetryPolicy: job.RetryPolicy.ToDomain(),
|
||||
Attempt: job.Attempt,
|
||||
QueueEligibleAt: job.QueueEligibleAt,
|
||||
NextAttemptAt: job.NextAttemptAt,
|
||||
LeaseTokenHash: job.LeaseTokenHash,
|
||||
LeaseSessionGen: job.LeaseSessionGen,
|
||||
AckDeadlineAt: job.AckDeadlineAt,
|
||||
LeaseExpiresAt: job.LeaseExpiresAt,
|
||||
LastProgressSeq: job.LastProgressSeq,
|
||||
CancelReason: job.CancelReason,
|
||||
CancelRequestedAt: job.CancelRequestedAt,
|
||||
CancelCompletedAt: job.CancelCompletedAt,
|
||||
TerminalAt: job.TerminalAt,
|
||||
TerminalFingerprint: job.TerminalFingerprint,
|
||||
LastReconciledAt: job.LastReconciledAt,
|
||||
ReconcileCount: job.ReconcileCount,
|
||||
ReconcileOutcome: job.ReconcileOutcome,
|
||||
CreatedAt: job.CreatedAt,
|
||||
UpdatedAt: job.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func executionInputFromDomain(input domain.JobExecutionInput) JobExecutionInput {
|
||||
return JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds}
|
||||
}
|
||||
|
||||
func (input JobExecutionInput) ToDomain() domain.JobExecutionInput {
|
||||
return domain.JobExecutionInput{WorkspaceScope: input.WorkspaceScope, Content: input.Content, ExpectedVersion: input.ExpectedVersion, ExpectedChecksum: input.ExpectedChecksum, MaxReadBytes: input.MaxReadBytes, RemoteAdapterKey: input.RemoteAdapterKey, RemoteAdapterKind: input.RemoteAdapterKind, TimeoutSeconds: input.TimeoutSeconds}
|
||||
}
|
||||
|
||||
func executionResultFromDomain(result domain.JobExecutionResult) JobExecutionResult {
|
||||
return JobExecutionResult{Kind: result.Kind, ProcessState: result.ProcessState, ExitClassification: result.ExitClassification, ExitCode: result.ExitCode, Version: result.Version, Checksum: result.Checksum, SizeBytes: result.SizeBytes, AuditSummary: result.AuditSummary, Content: result.Content}
|
||||
}
|
||||
|
||||
func (result JobExecutionResult) ToDomain() domain.JobExecutionResult {
|
||||
return domain.JobExecutionResult{Kind: result.Kind, ProcessState: result.ProcessState, ExitClassification: result.ExitClassification, ExitCode: result.ExitCode, Version: result.Version, Checksum: result.Checksum, SizeBytes: result.SizeBytes, AuditSummary: result.AuditSummary, Content: result.Content}
|
||||
}
|
||||
|
||||
func (policy JobRetryPolicy) ToDomain() domain.JobRetryPolicy {
|
||||
return domain.JobRetryPolicy{
|
||||
MaxAttempts: policy.MaxAttempts,
|
||||
InitialBackoffSeconds: policy.InitialBackoffSeconds,
|
||||
MaxBackoffSeconds: policy.MaxBackoffSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func retryPolicyFromDomain(policy domain.JobRetryPolicy) JobRetryPolicy {
|
||||
return JobRetryPolicy{
|
||||
MaxAttempts: policy.MaxAttempts,
|
||||
InitialBackoffSeconds: policy.InitialBackoffSeconds,
|
||||
MaxBackoffSeconds: policy.MaxBackoffSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user