mirror of
https://github.com/grafana/grafana.git
synced 2024-12-28 18:01:40 -06:00
CloudMigration: Interpret error code from migration resource item (#94407)
* start on loading the error code * error code to message mapping * use resource code type * use defined error code * partial updates from comments * i18nKey gen * fixed t * fixed translations * typing
This commit is contained in:
parent
1051561154
commit
a50507e645
@ -390,6 +390,7 @@ func (cma *CloudMigrationAPI) GetSnapshot(c *contextmodel.ReqContext) response.R
|
||||
RefID: results[i].RefID,
|
||||
Status: ItemStatus(results[i].Status),
|
||||
Message: results[i].Error,
|
||||
ErrorCode: ItemErrorCode(results[i].ErrorCode),
|
||||
ParentName: results[i].ParentName,
|
||||
}
|
||||
}
|
||||
|
@ -113,8 +113,9 @@ type MigrateDataResponseItemDTO struct {
|
||||
// required:true
|
||||
RefID string `json:"refId"`
|
||||
// required:true
|
||||
Status ItemStatus `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Status ItemStatus `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
ErrorCode ItemErrorCode `json:"errorCode,omitempty"`
|
||||
}
|
||||
|
||||
// swagger:enum MigrateDataType
|
||||
@ -143,6 +144,21 @@ const (
|
||||
ItemStatusUnknown ItemStatus = "UNKNOWN"
|
||||
)
|
||||
|
||||
// swagger:enum ItemErrorCode
|
||||
type ItemErrorCode string
|
||||
|
||||
const (
|
||||
ErrDatasourceNameConflict ItemErrorCode = "DATASOURCE_NAME_CONFLICT"
|
||||
ErrDashboardAlreadyManaged ItemErrorCode = "DASHBOARD_ALREADY_MANAGED"
|
||||
ErrLibraryElementNameConflict ItemErrorCode = "LIBRARY_ELEMENT_NAME_CONFLICT"
|
||||
ErrUnsupportedDataType ItemErrorCode = "UNSUPPORTED_DATA_TYPE"
|
||||
ErrResourceConflict ItemErrorCode = "RESOURCE_CONFLICT"
|
||||
ErrUnexpectedStatus ItemErrorCode = "UNEXPECTED_STATUS_CODE"
|
||||
ErrInternalServiceError ItemErrorCode = "INTERNAL_SERVICE_ERROR"
|
||||
ErrOnlyCoreDataSources ItemErrorCode = "ONLY_CORE_DATA_SOURCES"
|
||||
ErrGeneric ItemErrorCode = "GENERIC_ERROR"
|
||||
)
|
||||
|
||||
// swagger:parameters getCloudMigrationRun
|
||||
type GetMigrationRunParams struct {
|
||||
// RunUID of a migration run
|
||||
|
@ -573,6 +573,13 @@ func (s *Service) GetSnapshot(ctx context.Context, query cloudmigration.GetSnaps
|
||||
s.log.Error("error applying plugin warnings, please open a bug report: %w", err)
|
||||
}
|
||||
|
||||
// Log the errors for resources with errors at migration
|
||||
for _, resource := range resources {
|
||||
if resource.Status == cloudmigration.ItemStatusError && resource.Error != "" {
|
||||
s.log.Error("Could not migrate resource", "resourceID", resource.RefID, "error", resource.Error)
|
||||
}
|
||||
}
|
||||
|
||||
// We need to update the snapshot in our db before reporting anything
|
||||
if err := s.store.UpdateSnapshot(ctx, cloudmigration.UpdateSnapshotCmd{
|
||||
UID: snapshot.UID,
|
||||
@ -832,6 +839,7 @@ func (s *Service) getResourcesWithPluginWarnings(ctx context.Context, results []
|
||||
// if the plugin is not found, it means it was uninstalled, meaning it wasn't core
|
||||
if !p.IsCorePlugin() || !found {
|
||||
r.Status = cloudmigration.ItemStatusWarning
|
||||
r.ErrorCode = cloudmigration.ErrOnlyCoreDataSources
|
||||
r.Error = "Only core data sources are supported. Please ensure the plugin is installed on the cloud stack."
|
||||
}
|
||||
|
||||
|
@ -312,11 +312,11 @@ func (ss *sqlStore) GetSnapshotList(ctx context.Context, query cloudmigration.Li
|
||||
// If the uid is not known, it uses snapshot_uid + resource_uid as a lookup
|
||||
func (ss *sqlStore) CreateUpdateSnapshotResources(ctx context.Context, snapshotUid string, resources []cloudmigration.CloudMigrationResource) error {
|
||||
return ss.db.InTransaction(ctx, func(ctx context.Context) error {
|
||||
sql := "UPDATE cloud_migration_resource SET status=?, error_string=? WHERE uid=? OR (snapshot_uid=? AND resource_uid=?)"
|
||||
sql := "UPDATE cloud_migration_resource SET status=?, error_string=?, error_code=? WHERE uid=? OR (snapshot_uid=? AND resource_uid=?)"
|
||||
err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
for _, r := range resources {
|
||||
// try an update first
|
||||
result, err := sess.Exec(sql, r.Status, r.Error, r.UID, snapshotUid, r.RefID)
|
||||
result, err := sess.Exec(sql, r.Status, r.Error, r.ErrorCode, r.UID, snapshotUid, r.RefID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -68,11 +68,12 @@ type CloudMigrationResource struct {
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
UID string `xorm:"uid"`
|
||||
|
||||
Name string `xorm:"name" json:"name"`
|
||||
Type MigrateDataType `xorm:"resource_type" json:"type"`
|
||||
RefID string `xorm:"resource_uid" json:"refId"`
|
||||
Status ItemStatus `xorm:"status" json:"status"`
|
||||
Error string `xorm:"error_string" json:"error"`
|
||||
Name string `xorm:"name" json:"name"`
|
||||
Type MigrateDataType `xorm:"resource_type" json:"type"`
|
||||
RefID string `xorm:"resource_uid" json:"refId"`
|
||||
Status ItemStatus `xorm:"status" json:"status"`
|
||||
Error string `xorm:"error_string" json:"error"`
|
||||
ErrorCode ResourceErrorCode `xorm:"error_code" json:"error_code"`
|
||||
|
||||
SnapshotUID string `xorm:"snapshot_uid"`
|
||||
ParentName string `xorm:"parent_name" json:"parentName"`
|
||||
@ -101,6 +102,20 @@ const (
|
||||
ItemStatusPending ItemStatus = "PENDING"
|
||||
)
|
||||
|
||||
type ResourceErrorCode string
|
||||
|
||||
const (
|
||||
ErrDatasourceNameConflict ResourceErrorCode = "DATASOURCE_NAME_CONFLICT"
|
||||
ErrDashboardAlreadyManaged ResourceErrorCode = "DASHBOARD_ALREADY_MANAGED"
|
||||
ErrLibraryElementNameConflict ResourceErrorCode = "LIBRARY_ELEMENT_NAME_CONFLICT"
|
||||
ErrUnsupportedDataType ResourceErrorCode = "UNSUPPORTED_DATA_TYPE"
|
||||
ErrResourceConflict ResourceErrorCode = "RESOURCE_CONFLICT"
|
||||
ErrUnexpectedStatus ResourceErrorCode = "UNEXPECTED_STATUS_CODE"
|
||||
ErrInternalServiceError ResourceErrorCode = "INTERNAL_SERVICE_ERROR"
|
||||
ErrOnlyCoreDataSources ResourceErrorCode = "ONLY_CORE_DATA_SOURCES"
|
||||
ErrGeneric ResourceErrorCode = "GENERIC_ERROR"
|
||||
)
|
||||
|
||||
type SnapshotResourceStats struct {
|
||||
CountsByType map[MigrateDataType]int
|
||||
CountsByStatus map[ItemStatus]int
|
||||
|
@ -170,4 +170,10 @@ func addCloudMigrationsMigrations(mg *Migrator) {
|
||||
Type: DB_Text,
|
||||
Nullable: true,
|
||||
}))
|
||||
|
||||
mg.AddMigration("add cloud_migration_resource.error_code column", NewAddColumnMigration(migrationResourceTable, &Column{
|
||||
Name: "error_code",
|
||||
Type: DB_Text,
|
||||
Nullable: true,
|
||||
}))
|
||||
}
|
||||
|
@ -5769,6 +5769,20 @@
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"errorCode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"DATASOURCE_NAME_CONFLICT",
|
||||
"DASHBOARD_ALREADY_MANAGED",
|
||||
"LIBRARY_ELEMENT_NAME_CONFLICT",
|
||||
"UNSUPPORTED_DATA_TYPE",
|
||||
"RESOURCE_CONFLICT",
|
||||
"UNEXPECTED_STATUS_CODE",
|
||||
"INTERNAL_SERVICE_ERROR",
|
||||
"ONLY_CORE_DATA_SOURCES",
|
||||
"GENERIC_ERROR"
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -17262,6 +17262,20 @@
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"errorCode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"DATASOURCE_NAME_CONFLICT",
|
||||
"DASHBOARD_ALREADY_MANAGED",
|
||||
"LIBRARY_ELEMENT_NAME_CONFLICT",
|
||||
"UNSUPPORTED_DATA_TYPE",
|
||||
"RESOURCE_CONFLICT",
|
||||
"UNEXPECTED_STATUS_CODE",
|
||||
"INTERNAL_SERVICE_ERROR",
|
||||
"ONLY_CORE_DATA_SOURCES",
|
||||
"GENERIC_ERROR"
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -164,6 +164,16 @@ export type CreateSnapshotResponseDto = {
|
||||
uid?: string;
|
||||
};
|
||||
export type MigrateDataResponseItemDto = {
|
||||
errorCode?:
|
||||
| 'DATASOURCE_NAME_CONFLICT'
|
||||
| 'DASHBOARD_ALREADY_MANAGED'
|
||||
| 'LIBRARY_ELEMENT_NAME_CONFLICT'
|
||||
| 'UNSUPPORTED_DATA_TYPE'
|
||||
| 'RESOURCE_CONFLICT'
|
||||
| 'UNEXPECTED_STATUS_CODE'
|
||||
| 'INTERNAL_SERVICE_ERROR'
|
||||
| 'ONLY_CORE_DATA_SOURCES'
|
||||
| 'GENERIC_ERROR';
|
||||
message?: string;
|
||||
name?: string;
|
||||
parentName?: string;
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Button, Modal, Stack, Text } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
|
||||
import { MigrateDataResponseItemDto } from '../api';
|
||||
|
||||
import { prettyTypeName } from './TypeCell';
|
||||
import { ResourceTableItem } from './types';
|
||||
|
||||
@ -9,11 +11,65 @@ interface ResourceDetailsModalProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function getTMessage(errorCode: MigrateDataResponseItemDto['errorCode']): string {
|
||||
switch (errorCode) {
|
||||
case 'DATASOURCE_NAME_CONFLICT':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.datasource-name-conflict',
|
||||
'There is a data source with the same name in the target instance. Rename one of them and try again.'
|
||||
);
|
||||
case 'DASHBOARD_ALREADY_MANAGED':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.dashboard-already-managed',
|
||||
'Dashboard is already provisioned and managed by Grafana in the cloud instance. We recommend using the provisioned dashboard going forward. If you still wish to copy the dashboard to the cloud instance, then change the dashboard ID in the dashboard JSON, save a new snapshot and upload again.'
|
||||
);
|
||||
case 'LIBRARY_ELEMENT_NAME_CONFLICT':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.library-element-name-conflict',
|
||||
'There is a library element with the same name in the target instance. Rename one of them and try again.'
|
||||
);
|
||||
case 'UNSUPPORTED_DATA_TYPE':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.unsupported-data-type',
|
||||
'Migration of this data type is not currently supported.'
|
||||
);
|
||||
case 'RESOURCE_CONFLICT':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.resource-conflict',
|
||||
'There is a resource conflict with the target instance. Please check the Grafana server logs for more details.'
|
||||
);
|
||||
case 'ONLY_CORE_DATA_SOURCES':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.only-core-data-sources',
|
||||
'Only core data sources are supported. Please ensure the plugin is installed on the cloud stack.'
|
||||
);
|
||||
case 'UNEXPECTED_STATUS_CODE':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.unexpected-error',
|
||||
'There has been an error while migrating. Please check the Grafana server logs for more details.'
|
||||
);
|
||||
case 'INTERNAL_SERVICE_ERROR':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.internal-service-error',
|
||||
'There has been an error while migrating. Please check the Grafana server logs for more details.'
|
||||
);
|
||||
case 'GENERIC_ERROR':
|
||||
return t(
|
||||
'migrate-to-cloud.resource-details.error-messages.generic-error',
|
||||
'There has been an error while migrating. Please check the cloud migration logs for more information.'
|
||||
);
|
||||
// Handle new errors here
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function ResourceDetailsModal(props: ResourceDetailsModalProps) {
|
||||
const { resource, onClose } = props;
|
||||
|
||||
const refId = resource?.refId;
|
||||
const typeName = resource && prettyTypeName(resource.type);
|
||||
const hasError = resource?.errorCode || resource?.message;
|
||||
|
||||
let msgTitle = t('migrate-to-cloud.resource-details.generic-title', 'Resource migration details:');
|
||||
if (resource?.status === 'ERROR') {
|
||||
@ -36,12 +92,13 @@ export function ResourceDetailsModal(props: ResourceDetailsModalProps) {
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
{resource.message ? (
|
||||
{hasError ? (
|
||||
<>
|
||||
<Text element="p">{msgTitle}</Text>
|
||||
|
||||
<Text element="p" weight="bold">
|
||||
{resource.message}
|
||||
<Text element="p">
|
||||
{getTMessage(resource?.errorCode) ||
|
||||
resource?.message ||
|
||||
'There has been an error while migrating. Please check the cloud migration logs for more information.'}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
|
@ -1472,6 +1472,17 @@
|
||||
},
|
||||
"resource-details": {
|
||||
"dismiss-button": "OK",
|
||||
"error-messages": {
|
||||
"dashboard-already-managed": "Dashboard is already provisioned and managed by Grafana in the cloud instance. We recommend using the provisioned dashboard going forward. If you still wish to copy the dashboard to the cloud instance, then change the dashboard ID in the dashboard JSON, save a new snapshot and upload again.",
|
||||
"datasource-name-conflict": "There is a data source with the same name in the target instance. Rename one of them and try again.",
|
||||
"generic-error": "There has been an error while migrating. Please check the cloud migration logs for more information.",
|
||||
"internal-service-error": "There has been an error while migrating. Please check the Grafana server logs for more details.",
|
||||
"library-element-name-conflict": "There is a library element with the same name in the target instance. Rename one of them and try again.",
|
||||
"only-core-data-sources": "Only core data sources are supported. Please ensure the plugin is installed on the cloud stack.",
|
||||
"resource-conflict": "There is a resource conflict with the target instance. Please check the Grafana server logs for more details.",
|
||||
"unexpected-error": "There has been an error while migrating. Please check the Grafana server logs for more details.",
|
||||
"unsupported-data-type": "Migration of this data type is not currently supported."
|
||||
},
|
||||
"error-title": "Unable to migrate this resource:",
|
||||
"generic-title": "Resource migration details:",
|
||||
"missing-message": "No message provided.",
|
||||
|
@ -1472,6 +1472,17 @@
|
||||
},
|
||||
"resource-details": {
|
||||
"dismiss-button": "ØĶ",
|
||||
"error-messages": {
|
||||
"dashboard-already-managed": "Đäşĥþőäřđ įş äľřęäđy přővįşįőʼnęđ äʼnđ mäʼnäģęđ þy Ğřäƒäʼnä įʼn ŧĥę čľőūđ įʼnşŧäʼnčę. Ŵę řęčőmmęʼnđ ūşįʼnģ ŧĥę přővįşįőʼnęđ đäşĥþőäřđ ģőįʼnģ ƒőřŵäřđ. Ĩƒ yőū şŧįľľ ŵįşĥ ŧő čőpy ŧĥę đäşĥþőäřđ ŧő ŧĥę čľőūđ įʼnşŧäʼnčę, ŧĥęʼn čĥäʼnģę ŧĥę đäşĥþőäřđ ĨĐ įʼn ŧĥę đäşĥþőäřđ ĴŜØŃ, şävę ä ʼnęŵ şʼnäpşĥőŧ äʼnđ ūpľőäđ äģäįʼn.",
|
||||
"datasource-name-conflict": "Ŧĥęřę įş ä đäŧä şőūřčę ŵįŧĥ ŧĥę şämę ʼnämę įʼn ŧĥę ŧäřģęŧ įʼnşŧäʼnčę. Ŗęʼnämę őʼnę őƒ ŧĥęm äʼnđ ŧřy äģäįʼn.",
|
||||
"generic-error": "Ŧĥęřę ĥäş þęęʼn äʼn ęřřőř ŵĥįľę mįģřäŧįʼnģ. Pľęäşę čĥęčĸ ŧĥę čľőūđ mįģřäŧįőʼn ľőģş ƒőř mőřę įʼnƒőřmäŧįőʼn.",
|
||||
"internal-service-error": "Ŧĥęřę ĥäş þęęʼn äʼn ęřřőř ŵĥįľę mįģřäŧįʼnģ. Pľęäşę čĥęčĸ ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş.",
|
||||
"library-element-name-conflict": "Ŧĥęřę įş ä ľįþřäřy ęľęmęʼnŧ ŵįŧĥ ŧĥę şämę ʼnämę įʼn ŧĥę ŧäřģęŧ įʼnşŧäʼnčę. Ŗęʼnämę őʼnę őƒ ŧĥęm äʼnđ ŧřy äģäįʼn.",
|
||||
"only-core-data-sources": "Øʼnľy čőřę đäŧä şőūřčęş äřę şūppőřŧęđ. Pľęäşę ęʼnşūřę ŧĥę pľūģįʼn įş įʼnşŧäľľęđ őʼn ŧĥę čľőūđ şŧäčĸ.",
|
||||
"resource-conflict": "Ŧĥęřę įş ä řęşőūřčę čőʼnƒľįčŧ ŵįŧĥ ŧĥę ŧäřģęŧ įʼnşŧäʼnčę. Pľęäşę čĥęčĸ ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş.",
|
||||
"unexpected-error": "Ŧĥęřę ĥäş þęęʼn äʼn ęřřőř ŵĥįľę mįģřäŧįʼnģ. Pľęäşę čĥęčĸ ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş.",
|
||||
"unsupported-data-type": "Mįģřäŧįőʼn őƒ ŧĥįş đäŧä ŧypę įş ʼnőŧ čūřřęʼnŧľy şūppőřŧęđ."
|
||||
},
|
||||
"error-title": "Ůʼnäþľę ŧő mįģřäŧę ŧĥįş řęşőūřčę:",
|
||||
"generic-title": "Ŗęşőūřčę mįģřäŧįőʼn đęŧäįľş:",
|
||||
"missing-message": "Ńő męşşäģę přővįđęđ.",
|
||||
|
@ -7215,6 +7215,20 @@
|
||||
},
|
||||
"MigrateDataResponseItemDTO": {
|
||||
"properties": {
|
||||
"errorCode": {
|
||||
"enum": [
|
||||
"DATASOURCE_NAME_CONFLICT",
|
||||
"DASHBOARD_ALREADY_MANAGED",
|
||||
"LIBRARY_ELEMENT_NAME_CONFLICT",
|
||||
"UNSUPPORTED_DATA_TYPE",
|
||||
"RESOURCE_CONFLICT",
|
||||
"UNEXPECTED_STATUS_CODE",
|
||||
"INTERNAL_SERVICE_ERROR",
|
||||
"ONLY_CORE_DATA_SOURCES",
|
||||
"GENERIC_ERROR"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user