Provisioning: Add warning job status and different strictness (#108446)

* Add warning job status

* Different strictness for export than for pulling

* Fix some unit tests

* Fix unit tests

* Update frontend to display the warning status

* Fix status badge

* Add missing return statement

* Improve text in alert
This commit is contained in:
Roberto Jiménez Sánchez
2025-07-23 09:33:25 +00:00
committed by GitHub
parent 3fe21f1cca
commit 946358900c
24 changed files with 119 additions and 50 deletions
+4 -1
View File
@@ -54,10 +54,13 @@ const (
// Finished with errors
JobStateError JobState = "error"
// Finished with some non-critical errors
JobStateWarning JobState = "warning"
)
func (j JobState) Finished() bool {
return j == JobStateSuccess || j == JobStateError
return j == JobStateSuccess || j == JobStateError || j == JobStateWarning
}
type JobSpec struct {
@@ -890,10 +890,10 @@ func schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref common.ReferenceCallbac
Properties: map[string]spec.Schema{
"state": {
SchemaProps: spec.SchemaProps{
Description: "Possible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"working\"` The job is running",
Description: "Possible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"warning\"` Finished with some non-critical errors\n - `\"working\"` The job is running",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"error", "pending", "success", "working"},
Enum: []interface{}{"error", "pending", "success", "warning", "working"},
},
},
"started": {
@@ -2135,11 +2135,11 @@ func schema_pkg_apis_provisioning_v0alpha1_SyncStatus(ref common.ReferenceCallba
Properties: map[string]spec.Schema{
"state": {
SchemaProps: spec.SchemaProps{
Description: "pending, running, success, error\n\nPossible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"working\"` The job is running",
Description: "pending, running, success, error\n\nPossible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"warning\"` Finished with some non-critical errors\n - `\"working\"` The job is running",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"error", "pending", "success", "working"},
Enum: []interface{}{"error", "pending", "success", "warning", "working"},
},
},
"job": {
@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.4. DO NOT EDIT.
// Code generated by mockery v2.52.4. DO NOT EDIT.
package jobs
@@ -237,34 +237,35 @@ func (_c *MockJobProgressRecorder_SetTotal_Call) RunAndReturn(run func(context.C
return _c
}
// Strict provides a mock function with no fields
func (_m *MockJobProgressRecorder) Strict() {
_m.Called()
// StrictMaxErrors provides a mock function with given fields: maxErrors
func (_m *MockJobProgressRecorder) StrictMaxErrors(maxErrors int) {
_m.Called(maxErrors)
}
// MockJobProgressRecorder_Strict_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Strict'
type MockJobProgressRecorder_Strict_Call struct {
// MockJobProgressRecorder_StrictMaxErrors_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StrictMaxErrors'
type MockJobProgressRecorder_StrictMaxErrors_Call struct {
*mock.Call
}
// Strict is a helper method to define mock.On call
func (_e *MockJobProgressRecorder_Expecter) Strict() *MockJobProgressRecorder_Strict_Call {
return &MockJobProgressRecorder_Strict_Call{Call: _e.mock.On("Strict")}
// StrictMaxErrors is a helper method to define mock.On call
// - maxErrors int
func (_e *MockJobProgressRecorder_Expecter) StrictMaxErrors(maxErrors interface{}) *MockJobProgressRecorder_StrictMaxErrors_Call {
return &MockJobProgressRecorder_StrictMaxErrors_Call{Call: _e.mock.On("StrictMaxErrors", maxErrors)}
}
func (_c *MockJobProgressRecorder_Strict_Call) Run(run func()) *MockJobProgressRecorder_Strict_Call {
func (_c *MockJobProgressRecorder_StrictMaxErrors_Call) Run(run func(maxErrors int)) *MockJobProgressRecorder_StrictMaxErrors_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
run(args[0].(int))
})
return _c
}
func (_c *MockJobProgressRecorder_Strict_Call) Return() *MockJobProgressRecorder_Strict_Call {
func (_c *MockJobProgressRecorder_StrictMaxErrors_Call) Return() *MockJobProgressRecorder_StrictMaxErrors_Call {
_c.Call.Return()
return _c
}
func (_c *MockJobProgressRecorder_Strict_Call) RunAndReturn(run func()) *MockJobProgressRecorder_Strict_Call {
func (_c *MockJobProgressRecorder_StrictMaxErrors_Call) RunAndReturn(run func(int)) *MockJobProgressRecorder_StrictMaxErrors_Call {
_c.Run(run)
return _c
}
@@ -41,6 +41,10 @@ func (m *LegacyMigrator) Migrate(ctx context.Context, rw repository.ReaderWriter
Timeout: 10 * time.Minute,
}
// Fail if migrating at least one
progress.StrictMaxErrors(1)
progress.SetMessage(ctx, "migrating legacy resources")
if err := m.wrapWithStageFn(ctx, rw, stageOptions, func(repo repository.Repository, staged bool) error {
rw, ok := repo.(repository.ReaderWriter)
if !ok {
@@ -34,6 +34,8 @@ func TestWrapWithStageFn(t *testing.T) {
)
progress := jobs.NewMockJobProgressRecorder(t)
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
// Execute
repo := repository.NewMockRepository(t)
@@ -67,6 +69,8 @@ func TestWrapWithCloneFn_Error(t *testing.T) {
)
progress := jobs.NewMockJobProgressRecorder(t)
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
// Execute
repo := repository.NewMockRepository(t)
repo.On("Config").Return(&provisioning.Repository{
@@ -109,6 +113,8 @@ func TestLegacyMigrator_MigrateFails(t *testing.T) {
)
progress := jobs.NewMockJobProgressRecorder(t)
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
// Execute
repo := repository.NewMockRepository(t)
@@ -158,7 +164,9 @@ func TestLegacyMigrator_ResetUnifiedStorageFails(t *testing.T) {
)
progress := jobs.NewMockJobProgressRecorder(t)
progress.On("SetMessage", mock.Anything, mock.Anything).Return()
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
progress.On("SetMessage", mock.Anything, "resetting unified storage").Return()
// Execute
repo := repository.NewMockRepository(t)
@@ -213,8 +221,12 @@ func TestLegacyMigrator_SyncFails(t *testing.T) {
)
progress := jobs.NewMockJobProgressRecorder(t)
progress.On("SetMessage", mock.Anything, mock.Anything).Return()
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
progress.On("SetMessage", mock.Anything, "resetting unified storage").Return()
progress.On("ResetResults").Return()
progress.On("SetMessage", mock.Anything, "pulling resources").Return()
progress.On("SetMessage", mock.Anything, "error importing resources, reverting").Return()
// Execute
repo := repository.NewMockRepository(t)
@@ -268,8 +280,12 @@ func TestLegacyMigrator_SyncFails(t *testing.T) {
)
progress := jobs.NewMockJobProgressRecorder(t)
progress.On("SetMessage", mock.Anything, mock.Anything).Return()
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
progress.On("SetMessage", mock.Anything, "resetting unified storage").Return()
progress.On("ResetResults").Return()
progress.On("SetMessage", mock.Anything, "pulling resources").Return()
progress.On("SetMessage", mock.Anything, "error importing resources, reverting").Return()
// Execute
repo := repository.NewMockRepository(t)
@@ -321,8 +337,11 @@ func TestLegacyMigrator_Success(t *testing.T) {
)
progress := jobs.NewMockJobProgressRecorder(t)
progress.On("SetMessage", mock.Anything, mock.Anything).Return()
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
progress.On("SetMessage", mock.Anything, "resetting unified storage").Return()
progress.On("ResetResults").Return()
progress.On("SetMessage", mock.Anything, "pulling resources").Return()
// Execute
repo := repository.NewMockRepository(t)
@@ -364,6 +383,8 @@ func TestLegacyMigrator_BeforeFnExecution(t *testing.T) {
progress := jobs.NewMockJobProgressRecorder(t)
// No progress messages expected in current staging implementation
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
// Execute
repo := repository.NewMockRepository(t)
@@ -398,6 +419,8 @@ func TestLegacyMigrator_ProgressScanner(t *testing.T) {
progress := jobs.NewMockJobProgressRecorder(t)
// No progress messages expected in current staging implementation
progress.On("StrictMaxErrors", 1).Return()
progress.On("SetMessage", mock.Anything, "migrating legacy resources").Return()
repo := repository.NewMockRepository(t)
repo.On("Config").Return(&provisioning.Repository{
@@ -33,6 +33,8 @@ func NewUnifiedStorageMigrator(
func (m *UnifiedStorageMigrator) Migrate(ctx context.Context, repo repository.ReaderWriter, options provisioning.MigrateJobOptions, progress jobs.JobProgressRecorder) error {
namespace := repo.Config().GetNamespace()
progress.SetMessage(ctx, "export resources")
progress.StrictMaxErrors(1) // strict as we want the entire instance to be managed
exportJob := provisioning.Job{
Spec: provisioning.JobSpec{
Push: &provisioning.ExportJobOptions{},
@@ -30,6 +30,7 @@ func TestUnifiedStorageMigrator_Migrate(t *testing.T) {
},
})
pr.On("SetMessage", mock.Anything, "export resources").Return()
pr.On("StrictMaxErrors", 1).Return()
ew.On("Process", mock.Anything, rw, mock.MatchedBy(func(job provisioning.Job) bool {
return job.Spec.Push != nil
}), pr).Return(errors.New("export failed"))
@@ -46,6 +47,7 @@ func TestUnifiedStorageMigrator_Migrate(t *testing.T) {
},
})
pr.On("SetMessage", mock.Anything, "export resources").Return()
pr.On("StrictMaxErrors", 1).Return()
ew.On("Process", mock.Anything, rw, mock.MatchedBy(func(job provisioning.Job) bool {
return job.Spec.Push != nil
}), pr).Return(nil)
@@ -67,6 +69,7 @@ func TestUnifiedStorageMigrator_Migrate(t *testing.T) {
},
})
pr.On("SetMessage", mock.Anything, "export resources").Return()
pr.On("StrictMaxErrors", 1).Return()
nc.On("Clean", mock.Anything, "test-namespace", pr).Return(errors.New("clean failed"))
// Export and sync jobs succeed
@@ -92,6 +95,7 @@ func TestUnifiedStorageMigrator_Migrate(t *testing.T) {
},
})
pr.On("SetMessage", mock.Anything, "export resources").Return()
pr.On("StrictMaxErrors", 1).Return()
// Export job succeeds
ew.On("Process", mock.Anything, rw, mock.MatchedBy(func(job provisioning.Job) bool {
return job.Spec.Push != nil
@@ -43,7 +43,6 @@ func (w *MigrationWorker) Process(ctx context.Context, repo repository.Repositor
return errors.New("missing migrate settings")
}
progress.Strict()
progress.SetTotal(ctx, 10) // will show a progress bar
rw, ok := repo.(repository.ReaderWriter)
if !ok {
@@ -62,7 +62,6 @@ func TestMigrationWorker_ProcessNotReaderWriter(t *testing.T) {
}
progressRecorder := jobs.NewMockJobProgressRecorder(t)
progressRecorder.On("SetTotal", mock.Anything, 10).Return()
progressRecorder.On("Strict").Return()
repo := repository.NewMockReader(t)
err := worker.Process(context.Background(), repo, job, progressRecorder)
@@ -87,7 +86,6 @@ func TestMigrationWorker_WithHistory(t *testing.T) {
t.Run("fail local", func(t *testing.T) {
progressRecorder := jobs.NewMockJobProgressRecorder(t)
progressRecorder.On("SetTotal", mock.Anything, 10).Return()
progressRecorder.On("Strict").Return()
repo := local.NewLocal(&provisioning.Repository{}, nil)
err := worker.Process(context.Background(), repo, job, progressRecorder)
@@ -97,7 +95,6 @@ func TestMigrationWorker_WithHistory(t *testing.T) {
t.Run("fail unified", func(t *testing.T) {
progressRecorder := jobs.NewMockJobProgressRecorder(t)
progressRecorder.On("SetTotal", mock.Anything, 10).Return()
progressRecorder.On("Strict").Return()
repo := repository.NewMockRepository(t)
repo.On("Config").Return(&provisioning.Repository{
@@ -144,7 +141,6 @@ func TestMigrationWorker_Process(t *testing.T) {
isLegacyActive: true,
setupMocks: func(lm *MockMigrator, um *MockMigrator, ds *dualwrite.MockService, pr *jobs.MockJobProgressRecorder) {
pr.On("SetTotal", mock.Anything, 10).Return()
pr.On("Strict").Return()
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(false, nil)
lm.On("Migrate", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
},
@@ -160,7 +156,6 @@ func TestMigrationWorker_Process(t *testing.T) {
isLegacyActive: false,
setupMocks: func(lm *MockMigrator, um *MockMigrator, ds *dualwrite.MockService, pr *jobs.MockJobProgressRecorder) {
pr.On("SetTotal", mock.Anything, 10).Return()
pr.On("Strict").Return()
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(true, nil)
um.On("Migrate", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
},
@@ -176,7 +171,6 @@ func TestMigrationWorker_Process(t *testing.T) {
isLegacyActive: true,
setupMocks: func(lm *MockMigrator, um *MockMigrator, ds *dualwrite.MockService, pr *jobs.MockJobProgressRecorder) {
pr.On("SetTotal", mock.Anything, 10).Return()
pr.On("Strict").Return()
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(false, nil)
lm.On("Migrate", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("migration failed"))
},
@@ -60,8 +60,7 @@ type jobProgressRecorder struct {
func newJobProgressRecorder(ProgressFn ProgressFn) JobProgressRecorder {
return &jobProgressRecorder{
maxErrors: 20,
started: time.Now(),
started: time.Now(),
// Have a faster notifier for messages and total
notifyImmediatelyFn: maybeNotifyProgress(500*time.Millisecond, ProgressFn),
maybeNotifyFn: maybeNotifyProgress(5*time.Second, ProgressFn),
@@ -126,9 +125,9 @@ func (r *jobProgressRecorder) SetTotal(ctx context.Context, total int) {
r.notifyImmediately(ctx)
}
func (r *jobProgressRecorder) Strict() {
func (r *jobProgressRecorder) StrictMaxErrors(maxErrors int) {
r.mu.Lock()
r.maxErrors = 1
r.maxErrors = maxErrors
r.mu.Unlock()
}
@@ -136,7 +135,7 @@ func (r *jobProgressRecorder) TooManyErrors() error {
r.mu.RLock()
defer r.mu.RUnlock()
if r.errorCount >= r.maxErrors {
if r.maxErrors > 0 && r.errorCount >= r.maxErrors {
return fmt.Errorf("too many errors: %d", r.errorCount)
}
@@ -256,8 +255,13 @@ func (r *jobProgressRecorder) Complete(ctx context.Context, err error) provision
// Check for errors during execution
if len(jobStatus.Errors) > 0 && jobStatus.State != provisioning.JobStateError {
jobStatus.State = provisioning.JobStateError
jobStatus.Message = "completed with errors"
if r.TooManyErrors() != nil {
jobStatus.Message = "completed with too many errors"
jobStatus.State = provisioning.JobStateError
} else {
jobStatus.Message = "completed with errors"
jobStatus.State = provisioning.JobStateWarning
}
}
// Override message if progress have a more explicit message
@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.4. DO NOT EDIT.
// Code generated by mockery v2.52.4. DO NOT EDIT.
package jobs
+1 -1
View File
@@ -24,7 +24,7 @@ type JobProgressRecorder interface {
SetMessage(ctx context.Context, msg string)
SetTotal(ctx context.Context, total int)
TooManyErrors() error
Strict()
StrictMaxErrors(maxErrors int)
Complete(ctx context.Context, err error) provisioning.JobStatus
}
@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.4. DO NOT EDIT.
// Code generated by mockery v2.52.4. DO NOT EDIT.
package jobs
@@ -97,6 +97,8 @@ func (r *SyncWorker) Process(ctx context.Context, repo repository.Repository, jo
}
progress.SetMessage(ctx, "execute sync job")
progress.StrictMaxErrors(20) // make it stop after 20 errors
currentRef, syncError := r.syncer.Sync(ctx, rw, *job.Spec.Pull, repositoryResources, clients, progress)
jobStatus := progress.Complete(ctx, syncError)
syncStatus = jobStatus.ToSyncStatus(job.Name)
@@ -225,6 +225,7 @@ func TestSyncWorker_Process(t *testing.T) {
// Sync execution succeeds
pr.On("SetMessage", mock.Anything, "execute sync job").Return()
pr.On("StrictMaxErrors", 20).Return()
s.On("Sync", mock.Anything, rw, mock.MatchedBy(func(opts provisioning.SyncJobOptions) bool {
return true // Add specific sync options validation if needed
}), mockRepoResources, mock.Anything, pr).Return("new-ref", nil)
@@ -277,6 +278,7 @@ func TestSyncWorker_Process(t *testing.T) {
// Sync execution fails
pr.On("SetMessage", mock.Anything, "execute sync job").Return()
pr.On("StrictMaxErrors", 20).Return()
syncError := errors.New("sync operation failed")
s.On("Sync", mock.Anything, rw, mock.MatchedBy(func(opts provisioning.SyncJobOptions) bool {
return true // Add specific sync options validation if needed
@@ -317,6 +319,7 @@ func TestSyncWorker_Process(t *testing.T) {
mockClients := resources.NewMockResourceClients(t)
cf.On("Clients", mock.Anything, mock.Anything).Return(mockClients, nil)
pr.On("SetMessage", mock.Anything, mock.Anything).Return()
pr.On("StrictMaxErrors", 20).Return()
pr.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{State: provisioning.JobStateSuccess})
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything).Return(nil)
s.On("Sync", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("new-ref", nil)
@@ -348,6 +351,7 @@ func TestSyncWorker_Process(t *testing.T) {
mockClients := resources.NewMockResourceClients(t)
cf.On("Clients", mock.Anything, mock.Anything).Return(mockClients, nil)
pr.On("SetMessage", mock.Anything, mock.Anything).Return()
pr.On("StrictMaxErrors", 20).Return()
pr.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{State: provisioning.JobStateSuccess})
s.On("Sync", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("new-ref", nil)
},
@@ -407,6 +411,7 @@ func TestSyncWorker_Process(t *testing.T) {
mockClients := resources.NewMockResourceClients(t)
cf.On("Clients", mock.Anything, mock.Anything).Return(mockClients, nil)
pr.On("SetMessage", mock.Anything, mock.Anything).Return()
pr.On("StrictMaxErrors", 20).Return()
pr.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{State: provisioning.JobStateSuccess})
s.On("Sync", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("new-ref", nil)
},
@@ -459,6 +464,7 @@ func TestSyncWorker_Process(t *testing.T) {
mockClients := resources.NewMockResourceClients(t)
cf.On("Clients", mock.Anything, mock.Anything).Return(mockClients, nil)
pr.On("SetMessage", mock.Anything, mock.Anything).Return()
pr.On("StrictMaxErrors", 20).Return()
pr.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{State: provisioning.JobStateSuccess})
s.On("Sync", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("new-ref", nil)
},
@@ -489,6 +495,7 @@ func TestSyncWorker_Process(t *testing.T) {
// Sync succeeds
pr.On("SetMessage", mock.Anything, mock.Anything).Return()
pr.On("StrictMaxErrors", 20).Return()
s.On("Sync", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("new-ref", nil)
pr.On("Complete", mock.Anything, nil).Return(provisioning.JobStatus{State: provisioning.JobStateSuccess})
@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.4. DO NOT EDIT.
// Code generated by mockery v2.52.4. DO NOT EDIT.
package jobs
@@ -21,6 +21,7 @@ import (
var (
ErrAlreadyInRepository = errors.New("already in repository")
ErrDuplicateName = errors.New("duplicate name in repository")
ErrMissingName = field.Required(field.NewPath("name", "metadata", "name"), "missing name in resource")
)
@@ -152,7 +153,7 @@ func (r *ResourcesManager) WriteResourceFromFile(ctx context.Context, path strin
}
existing, found := r.resourcesLookup[id]
if found {
return "", parsed.GVK, fmt.Errorf("duplicate resource name: %s, %s and %s", parsed.Obj.GetName(), path, existing)
return "", parsed.GVK, fmt.Errorf("duplicate resource name: %s, %s and %s: %w", parsed.Obj.GetName(), path, existing, ErrDuplicateName)
}
r.resourcesLookup[id] = path
@@ -3096,12 +3096,13 @@
"format": "int64"
},
"state": {
"description": "Possible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"working\"` The job is running",
"description": "Possible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"warning\"` Finished with some non-critical errors\n - `\"working\"` The job is running",
"type": "string",
"enum": [
"error",
"pending",
"success",
"warning",
"working"
]
},
@@ -4048,13 +4049,14 @@
"format": "int64"
},
"state": {
"description": "pending, running, success, error\n\nPossible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"working\"` The job is running",
"description": "pending, running, success, error\n\nPossible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"warning\"` Finished with some non-critical errors\n - `\"working\"` The job is running",
"type": "string",
"default": "",
"enum": [
"error",
"pending",
"success",
"warning",
"working"
]
}
@@ -819,8 +819,9 @@ export type JobStatus = {
- `"error"` Finished with errors
- `"pending"` Job has been submitted, but not processed yet
- `"success"` Finished with success
- `"warning"` Finished with some non-critical errors
- `"working"` The job is running */
state?: 'error' | 'pending' | 'success' | 'working';
state?: 'error' | 'pending' | 'success' | 'warning' | 'working';
/** Summary of processed actions */
summary?: JobResourceSummary[];
};
@@ -991,8 +992,9 @@ export type SyncStatus = {
- `"error"` Finished with errors
- `"pending"` Job has been submitted, but not processed yet
- `"success"` Finished with success
- `"warning"` Finished with some non-critical errors
- `"working"` The job is running */
state: 'error' | 'pending' | 'success' | 'working';
state: 'error' | 'pending' | 'success' | 'warning' | 'working';
};
export type WebhookStatus = {
encryptedSecret?: string;
@@ -42,6 +42,9 @@ export function FinishedJobStatus({ jobUid, repositoryName }: FinishedJobProps)
});
} else if (job.status.state === 'success') {
setStepStatusInfo({ status: 'success' });
} else if (job.status.state === 'warning') {
// We treat warnings as success for now, but this could be changed later
setStepStatusInfo({ status: 'success' });
}
}
@@ -29,13 +29,22 @@ export function JobContent({ job, isFinishedJob = false }: JobContentProps) {
title={t('provisioning.job-status.status.title-job-completed-successfully', 'Job completed successfully')}
/>
);
case 'warning':
return (
<Alert
severity="warning"
title={t('provisioning.job-status.status.title-warning-running-job', 'Job completed with warnings')}
>
{errors?.length ? errors?.join('\n') : message}
</Alert>
);
case 'error':
return (
<Alert
severity="error"
title={t('provisioning.job-status.status.title-error-running-job', 'Error running job')}
>
{message ?? errors?.join('\n')}
{errors?.length ? errors?.join('\n') : message}
</Alert>
);
}
@@ -26,8 +26,11 @@ const getStatusColor = (state?: SyncStatus['state']) => {
case 'success':
return 'green';
case 'working':
case 'pending':
return 'blue';
case 'warning':
return 'orange';
case 'pending':
return 'darkgrey';
case 'error':
return 'red';
default:
@@ -27,7 +27,7 @@ export function StatusBadge({ repo }: StatusBadgeProps) {
text = 'Automatic pulling disabled';
icon = 'info-circle';
} else if (!repo.status?.sync?.state?.length) {
color = 'orange';
color = 'darkgrey';
text = 'Pending';
icon = 'spinner';
tooltip = 'Waiting for health check to run';
@@ -40,8 +40,13 @@ export function StatusBadge({ repo }: StatusBadgeProps) {
color = 'green';
break;
case 'working':
case 'pending':
case 'warning':
color = 'orange';
text = 'warning';
icon = 'exclamation-triangle';
break;
case 'pending':
color = 'darkgrey';
text = 'Pulling';
icon = 'spinner';
break;
+2 -1
View File
@@ -11208,7 +11208,8 @@
"starting": "Starting...",
"status": {
"title-error-running-job": "Error running job",
"title-job-completed-successfully": "Job completed successfully"
"title-job-completed-successfully": "Job completed successfully",
"title-warning-running-job": "Job completed with warnings"
},
"summary": "Summary"
},