2024-02-26 21:52:16 +08:00
package cloudmigration
import (
2024-03-25 20:30:47 +08:00
"time"
2024-06-13 12:11:35 +08:00
"github.com/grafana/grafana/pkg/apimachinery/errutil"
2024-02-26 21:52:16 +08:00
)
var (
2024-05-13 12:22:46 +08:00
ErrInternalNotImplementedError = errutil . Internal ( "cloudmigrations.notImplemented" ) . Errorf ( "Internal server error" )
ErrFeatureDisabledError = errutil . Internal ( "cloudmigrations.disabled" ) . Errorf ( "Cloud migrations are disabled on this instance" )
2024-06-14 01:58:59 +08:00
ErrMigrationNotFound = errutil . NotFound ( "cloudmigrations.sessionNotFound" ) . Errorf ( "Session not found" )
2024-05-13 12:22:46 +08:00
ErrMigrationRunNotFound = errutil . NotFound ( "cloudmigrations.migrationRunNotFound" ) . Errorf ( "Migration run not found" )
2024-06-14 01:58:59 +08:00
ErrMigrationNotDeleted = errutil . Internal ( "cloudmigrations.sessionNotDeleted" ) . Errorf ( "Session not deleted" )
2024-05-31 20:39:10 +08:00
ErrTokenNotFound = errutil . NotFound ( "cloudmigrations.tokenNotFound" ) . Errorf ( "Token not found" )
2024-06-19 21:20:52 +08:00
ErrSnapshotNotFound = errutil . NotFound ( "cloudmigrations.snapshotNotFound" ) . Errorf ( "Snapshot not found" )
2025-04-08 23:46:25 +08:00
ErrEmptyResourceTypes = errutil . BadRequest ( "cloudmigrations.emptyResourceTypes" ) . Errorf ( "Resource types cannot be empty" )
2024-02-26 21:52:16 +08:00
)
2024-06-14 01:58:59 +08:00
// CloudMigration domain structs
2024-07-10 20:46:38 +08:00
// CloudMigrationSession represents a configured migration token
2024-06-14 01:58:59 +08:00
type CloudMigrationSession struct {
ID int64 ` xorm:"pk autoincr 'id'" `
2024-11-13 03:03:16 +08:00
OrgID int64 ` xorm:"org_id" `
2024-06-14 01:58:59 +08:00
UID string ` xorm:"uid" `
AuthToken string
Slug string
StackID int ` xorm:"stack_id" `
RegionSlug string
ClusterSlug string
Created time . Time
Updated time . Time
}
2024-07-10 20:46:38 +08:00
// CloudMigrationSnapshot contains all of the metadata about a snapshot
2024-06-14 01:58:59 +08:00
type CloudMigrationSnapshot struct {
2025-07-25 22:41:21 +08:00
ID int64 ` xorm:"pk autoincr 'id'" `
UID string ` xorm:"uid" `
SessionUID string ` xorm:"session_uid" `
Status SnapshotStatus
GMSPublicKey [ ] byte ` xorm:"-" ` // stored in the unified secrets table
PublicKey [ ] byte ` xorm:"public_key" `
LocalDir string ` xorm:"local_directory" `
GMSSnapshotUID string ` xorm:"gms_snapshot_uid" `
ErrorString string ` xorm:"error_string" `
ResourceStorageType string ` xorm:"resource_storage_type" `
EncryptionAlgo string ` xorm:"encryption_algo" `
Metadata [ ] byte ` xorm:"'metadata'" `
Created time . Time
Updated time . Time
Finished time . Time
2024-06-19 21:20:52 +08:00
2024-06-25 11:50:07 +08:00
// Stored in the cloud_migration_resource table
Resources [ ] CloudMigrationResource ` xorm:"-" `
2024-07-10 20:46:38 +08:00
// Derived by querying the cloud_migration_resource table
StatsRollup SnapshotResourceStats ` xorm:"-" `
2024-06-14 01:58:59 +08:00
}
2025-07-25 22:41:21 +08:00
type CloudMigrationSnapshotPartition struct {
SnapshotUID string ` xorm:"snapshot_uid" `
ResourceType string ` xorm:"resource_type" `
PartitionNumber int ` xorm:"partition_number" `
Data [ ] byte ` xorm:"data" `
}
type CloudMigrationSnapshotIndex struct {
EncryptionAlgo string
PublicKey [ ] byte
Metadata [ ] byte
Items map [ string ] [ ] int
}
2024-06-19 21:20:52 +08:00
type SnapshotStatus string
const (
2024-07-15 21:22:57 +08:00
SnapshotStatusCreating SnapshotStatus = "creating"
SnapshotStatusPendingUpload SnapshotStatus = "pending_upload"
SnapshotStatusUploading SnapshotStatus = "uploading"
SnapshotStatusPendingProcessing SnapshotStatus = "pending_processing"
SnapshotStatusProcessing SnapshotStatus = "processing"
SnapshotStatusFinished SnapshotStatus = "finished"
SnapshotStatusCanceled SnapshotStatus = "canceled"
SnapshotStatusError SnapshotStatus = "error"
2024-06-19 21:20:52 +08:00
)
2024-06-25 11:50:07 +08:00
type CloudMigrationResource struct {
ID int64 ` xorm:"pk autoincr 'id'" `
2025-01-11 03:42:18 +08:00
UID string ` xorm:"uid" json:"uid" `
2024-06-25 11:50:07 +08:00
2024-10-17 21:09:09 +08:00
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" `
2024-06-25 11:50:07 +08:00
SnapshotUID string ` xorm:"snapshot_uid" `
2024-10-07 18:35:08 +08:00
ParentName string ` xorm:"parent_name" json:"parentName" `
2024-06-25 11:50:07 +08:00
}
2024-07-10 20:46:38 +08:00
type MigrateDataType string
const (
2024-10-07 18:52:31 +08:00
DashboardDataType MigrateDataType = "DASHBOARD"
DatasourceDataType MigrateDataType = "DATASOURCE"
FolderDataType MigrateDataType = "FOLDER"
LibraryElementDataType MigrateDataType = "LIBRARY_ELEMENT"
AlertRuleType MigrateDataType = "ALERT_RULE"
2025-02-06 19:35:59 +08:00
AlertRuleGroupType MigrateDataType = "ALERT_RULE_GROUP"
2024-10-07 18:52:31 +08:00
ContactPointType MigrateDataType = "CONTACT_POINT"
NotificationPolicyType MigrateDataType = "NOTIFICATION_POLICY"
NotificationTemplateType MigrateDataType = "NOTIFICATION_TEMPLATE"
MuteTimingType MigrateDataType = "MUTE_TIMING"
2025-01-06 18:37:15 +08:00
PluginDataType MigrateDataType = "PLUGIN"
2024-07-10 20:46:38 +08:00
)
type ItemStatus string
const (
2025-01-11 03:42:18 +08:00
// Returned by GMS
ItemStatusOK ItemStatus = "OK"
ItemStatusError ItemStatus = "ERROR"
// Used by default while awaiting GMS results
2024-07-10 20:46:38 +08:00
ItemStatusPending ItemStatus = "PENDING"
)
2024-10-17 21:09:09 +08:00
type ResourceErrorCode string
const (
2025-07-10 03:25:20 +08:00
ErrAlertRulesQuotaReached ResourceErrorCode = "ALERT_RULES_QUOTA_REACHED"
ErrAlertRulesGroupQuotaReached ResourceErrorCode = "ALERT_RULES_GROUP_QUOTA_REACHED"
ErrDatasourceNameConflict ResourceErrorCode = "DATASOURCE_NAME_CONFLICT"
ErrDatasourceInvalidURL ResourceErrorCode = "DATASOURCE_INVALID_URL"
ErrDatasourceAlreadyManaged ResourceErrorCode = "DATASOURCE_ALREADY_MANAGED"
ErrFolderNameConflict ResourceErrorCode = "FOLDER_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"
ErrGeneric ResourceErrorCode = "GENERIC_ERROR"
2024-10-17 21:09:09 +08:00
)
2024-07-10 20:46:38 +08:00
type SnapshotResourceStats struct {
CountsByType map [ MigrateDataType ] int
CountsByStatus map [ ItemStatus ] int
2024-07-16 21:04:21 +08:00
Total int
2024-07-10 20:46:38 +08:00
}
2024-06-19 21:20:52 +08:00
// Deprecated, use GetSnapshotResult for the async workflow
func ( s CloudMigrationSnapshot ) GetResult ( ) ( * MigrateDataResponse , error ) {
2024-06-25 11:50:07 +08:00
result := MigrateDataResponse {
RunUID : s . UID ,
Items : s . Resources ,
2024-04-04 00:47:49 +08:00
}
return & result , nil
}
2024-06-19 21:20:52 +08:00
func ( s CloudMigrationSnapshot ) ShouldQueryGMS ( ) bool {
return s . Status == SnapshotStatusPendingProcessing || s . Status == SnapshotStatusProcessing
}
type CloudMigrationRunList struct {
2024-06-14 01:58:59 +08:00
Runs [ ] MigrateDataResponseList
2024-04-02 19:57:42 +08:00
}
2024-06-14 01:58:59 +08:00
type CloudMigrationSessionRequest struct {
AuthToken string
2024-11-13 03:03:16 +08:00
// OrgId in the on prem instance
OrgID int64
2024-03-25 20:30:47 +08:00
}
2024-06-14 01:58:59 +08:00
type CloudMigrationSessionResponse struct {
UID string
Slug string
Created time . Time
Updated time . Time
2024-03-25 20:30:47 +08:00
}
2024-06-14 01:58:59 +08:00
type CloudMigrationSessionListResponse struct {
Sessions [ ] CloudMigrationSessionResponse
2024-03-28 19:50:31 +08:00
}
2025-03-30 22:42:45 +08:00
type ResultSortColumn string
const (
SortColumnID ResultSortColumn = "id"
SortColumnName ResultSortColumn = "name"
SortColumnType ResultSortColumn = "resource_type"
SortColumnStatus ResultSortColumn = "status"
)
type SortOrder string
const (
SortOrderAsc SortOrder = "ASC"
SortOrderDesc SortOrder = "DESC"
)
// ResultPage should be in the range [1, 10000]
type ResultPage int
// ResultLimit should be in the rage [1, 10000]
type ResultLimit int
type SnapshotResultQueryParams struct {
ResultPage ResultPage
ResultLimit ResultLimit
SortColumn ResultSortColumn
SortOrder SortOrder
ErrorsOnly bool
}
2025-04-08 23:46:25 +08:00
type ResourceTypes map [ MigrateDataType ] struct { }
func ( r ResourceTypes ) Has ( t MigrateDataType ) bool {
_ , ok := r [ t ]
return ok
}
type CreateSnapshotCommand struct {
SessionUID string
ResourceTypes ResourceTypes
}
2024-06-25 11:50:07 +08:00
type GetSnapshotsQuery struct {
SnapshotUID string
2024-11-13 03:03:16 +08:00
OrgID int64
2024-06-25 11:50:07 +08:00
SessionUID string
2025-03-30 22:42:45 +08:00
SnapshotResultQueryParams
2024-06-25 11:50:07 +08:00
}
2024-06-19 21:20:52 +08:00
type ListSnapshotsQuery struct {
SessionUID string
2024-11-13 03:03:16 +08:00
OrgID int64
2024-06-25 11:50:07 +08:00
Page int
2024-06-19 21:20:52 +08:00
Limit int
2024-10-01 16:28:25 +08:00
Sort string
2024-06-19 21:20:52 +08:00
}
type UpdateSnapshotCmd struct {
2024-06-25 11:50:07 +08:00
UID string
2024-07-30 21:02:41 +08:00
SessionID string
2024-06-25 11:50:07 +08:00
Status SnapshotStatus
2025-07-25 22:41:21 +08:00
PublicKey [ ] byte
2025-01-11 03:42:18 +08:00
// LocalResourcesToCreate represents the local state of a resource before it has been uploaded to GMS
LocalResourcesToCreate [ ] CloudMigrationResource
// CloudResourcesToUpdate represents resource state from GMS, to be merged with the local state
CloudResourcesToUpdate [ ] CloudMigrationResource
2024-06-19 21:20:52 +08:00
}
2024-04-03 19:36:13 +08:00
// access token
2024-03-25 23:43:28 +08:00
type CreateAccessTokenResponse struct {
Token string
}
2024-02-26 21:52:16 +08:00
2024-03-29 03:02:54 +08:00
type Base64EncodedTokenPayload struct {
Token string
Instance Base64HGInstance
}
2024-11-13 03:03:16 +08:00
func ( p Base64EncodedTokenPayload ) ToMigration ( orgID int64 ) CloudMigrationSession {
2024-06-14 01:58:59 +08:00
return CloudMigrationSession {
2024-03-29 08:55:27 +08:00
AuthToken : p . Token ,
2024-06-14 01:58:59 +08:00
Slug : p . Instance . Slug ,
2024-03-29 08:55:27 +08:00
StackID : p . Instance . StackID ,
RegionSlug : p . Instance . RegionSlug ,
ClusterSlug : p . Instance . ClusterSlug ,
2024-11-13 03:03:16 +08:00
OrgID : orgID ,
2024-03-29 08:55:27 +08:00
}
}
2024-03-29 03:02:54 +08:00
type Base64HGInstance struct {
StackID int
Slug string
RegionSlug string
ClusterSlug string
}
2024-04-03 19:36:13 +08:00
2024-06-14 01:58:59 +08:00
// GMS domain structs
2024-04-03 19:36:13 +08:00
2024-06-14 01:58:59 +08:00
type MigrateDataRequest struct {
2024-10-07 18:35:08 +08:00
Items [ ] MigrateDataRequestItem
ItemParentNames map [ MigrateDataType ] map [ string ] ( string )
2024-04-03 19:36:13 +08:00
}
2024-06-14 01:58:59 +08:00
type MigrateDataRequestItem struct {
Type MigrateDataType
RefID string
Name string
Data interface { }
2024-04-03 19:36:13 +08:00
}
2024-06-14 01:58:59 +08:00
type MigrateDataResponse struct {
RunUID string
2024-06-25 11:50:07 +08:00
Items [ ] CloudMigrationResource
2024-04-03 19:36:13 +08:00
}
2024-06-14 01:58:59 +08:00
type MigrateDataResponseList struct {
RunUID string
2024-05-01 02:44:35 +08:00
}
2024-06-19 21:20:52 +08:00
type CreateSessionResponse struct {
SnapshotUid string
}
2024-07-03 21:38:26 +08:00
type StartSnapshotResponse struct {
2024-07-11 20:32:02 +08:00
SnapshotID string ` json:"snapshotID" `
MaxItemsPerPartition uint32 ` json:"maxItemsPerPartition" `
Algo string ` json:"algo" `
2025-07-25 22:41:21 +08:00
GMSPublicKey [ ] byte ` json:"encryptionKey" `
2024-07-22 21:11:57 +08:00
Metadata [ ] byte ` json:"metadata" `
2024-06-19 21:20:52 +08:00
}
2024-07-15 21:22:57 +08:00
// Based on Grafana Migration Service DTOs
type GetSnapshotStatusResponse struct {
State SnapshotState ` json:"state" `
Results [ ] CloudMigrationResource ` json:"results" `
}
type SnapshotState string
const (
SnapshotStateInitialized SnapshotState = "INITIALIZED"
SnapshotStateProcessing SnapshotState = "PROCESSING"
SnapshotStateFinished SnapshotState = "FINISHED"
SnapshotStateCanceled SnapshotState = "CANCELED"
SnapshotStateError SnapshotState = "ERROR"
SnapshotStateUnknown SnapshotState = "UNKNOWN"
)
2024-10-21 16:45:54 +08:00
var (
ErrTokenInvalid = errutil . Internal ( "cloudmigrations.createMigration.tokenInvalid" , errutil . WithPublicMessage ( "Token is not valid. Generate a new token on your cloud instance and try again." ) )
ErrTokenRequestError = errutil . Internal ( "cloudmigrations.createMigration.tokenRequestError" , errutil . WithPublicMessage ( "An error occurred while validating the token. Please check the Grafana instance logs." ) )
ErrTokenValidationFailure = errutil . Internal ( "cloudmigrations.createMigration.tokenValidationFailure" , errutil . WithPublicMessage ( "Token is not valid. Please ensure the token matches the migration token on your cloud instance." ) )
ErrInstanceUnreachable = errutil . Internal ( "cloudmigrations.createMigration.instanceUnreachable" , errutil . WithPublicMessage ( "The cloud instance cannot be reached. Make sure the instance is running and try again." ) )
ErrInstanceRequestError = errutil . Internal ( "cloudmigrations.createMigration.instanceRequestError" , errutil . WithPublicMessage ( "An error occurred while attempting to verify the cloud instance's connectivity. Please check the network settings or cloud instance status." ) )
ErrSessionCreationFailure = errutil . Internal ( "cloudmigrations.createMigration.sessionCreationFailure" , errutil . WithPublicMessage ( "There was an error creating the migration. Please try again." ) )
ErrMigrationDisabled = errutil . Internal ( "cloudmigrations.createMigration.migrationDisabled" , errutil . WithPublicMessage ( "Cloud migrations are disabled on this instance." ) )
)