2020-08-18 07:23:45 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-08-18 07:23:45 +03:00
package cmd
import (
"context"
2024-06-05 07:00:56 +03:00
"errors"
2020-08-18 07:23:45 +03:00
"fmt"
2024-06-05 07:00:56 +03:00
"io/fs"
2020-09-29 12:05:13 +03:00
"strings"
2020-08-18 07:23:45 +03:00
2023-05-12 16:30:28 +03:00
actions_model "code.gitea.io/gitea/models/actions"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2022-06-12 18:51:54 +03:00
git_model "code.gitea.io/gitea/models/git"
2020-08-18 07:23:45 +03:00
"code.gitea.io/gitea/models/migrations"
2022-08-16 07:05:15 +03:00
packages_model "code.gitea.io/gitea/models/packages"
2021-11-19 16:39:57 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2020-08-18 07:23:45 +03:00
"code.gitea.io/gitea/modules/log"
2022-08-16 07:05:15 +03:00
packages_module "code.gitea.io/gitea/modules/packages"
2020-08-18 07:23:45 +03:00
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
2023-07-21 12:28:19 +03:00
"github.com/urfave/cli/v2"
2020-08-18 07:23:45 +03:00
)
// CmdMigrateStorage represents the available migrate storage sub-command.
2023-07-21 12:28:19 +03:00
var CmdMigrateStorage = & cli . Command {
2020-08-18 07:23:45 +03:00
Name : "migrate-storage" ,
Usage : "Migrate the storage" ,
2022-08-16 07:05:15 +03:00
Description : "Copies stored files from storage configured in app.ini to parameter-configured storage" ,
2020-08-18 07:23:45 +03:00
Action : runMigrateStorage ,
Flags : [ ] cli . Flag {
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
Name : "type" ,
Aliases : [ ] string { "t" } ,
Value : "" ,
2024-05-25 01:32:36 +03:00
Usage : "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts'" ,
2020-08-18 07:23:45 +03:00
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
Name : "storage" ,
Aliases : [ ] string { "s" } ,
Value : "" ,
Usage : "New storage type: local (default) or minio" ,
2020-08-18 07:23:45 +03:00
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
Name : "path" ,
Aliases : [ ] string { "p" } ,
Value : "" ,
Usage : "New storage placement if store is local (leave blank for default)" ,
2020-08-18 07:23:45 +03:00
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2020-08-18 07:23:45 +03:00
Name : "minio-endpoint" ,
Value : "" ,
Usage : "Minio storage endpoint" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2020-08-18 07:23:45 +03:00
Name : "minio-access-key-id" ,
Value : "" ,
Usage : "Minio storage accessKeyID" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2020-08-18 07:23:45 +03:00
Name : "minio-secret-access-key" ,
Value : "" ,
Usage : "Minio storage secretAccessKey" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2020-08-18 07:23:45 +03:00
Name : "minio-bucket" ,
Value : "" ,
Usage : "Minio storage bucket" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2020-08-18 07:23:45 +03:00
Name : "minio-location" ,
Value : "" ,
Usage : "Minio storage location to create bucket" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2020-08-18 07:23:45 +03:00
Name : "minio-base-path" ,
Value : "" ,
2023-03-28 18:10:24 +03:00
Usage : "Minio storage base path on the bucket" ,
2020-08-18 07:23:45 +03:00
} ,
2023-07-21 12:28:19 +03:00
& cli . BoolFlag {
2020-08-18 07:23:45 +03:00
Name : "minio-use-ssl" ,
Usage : "Enable SSL for minio" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . BoolFlag {
2023-03-28 18:10:24 +03:00
Name : "minio-insecure-skip-verify" ,
Usage : "Skip SSL verification" ,
} ,
2023-07-21 12:28:19 +03:00
& cli . StringFlag {
2023-03-28 18:10:24 +03:00
Name : "minio-checksum-algorithm" ,
Value : "" ,
Usage : "Minio checksum algorithm (default/md5)" ,
} ,
2020-08-18 07:23:45 +03:00
} ,
}
2022-08-16 07:05:15 +03:00
func migrateAttachments ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
2022-10-31 18:51:14 +03:00
return db . Iterate ( ctx , nil , func ( ctx context . Context , attach * repo_model . Attachment ) error {
2020-08-18 07:23:45 +03:00
_ , err := storage . Copy ( dstStorage , attach . RelativePath ( ) , storage . Attachments , attach . RelativePath ( ) )
return err
} )
}
2022-08-16 07:05:15 +03:00
func migrateLFS ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
2022-10-31 18:51:14 +03:00
return db . Iterate ( ctx , nil , func ( ctx context . Context , mo * git_model . LFSMetaObject ) error {
2020-09-08 18:45:10 +03:00
_ , err := storage . Copy ( dstStorage , mo . RelativePath ( ) , storage . LFS , mo . RelativePath ( ) )
return err
} )
}
2022-08-16 07:05:15 +03:00
func migrateAvatars ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
2022-10-31 18:51:14 +03:00
return db . Iterate ( ctx , nil , func ( ctx context . Context , user * user_model . User ) error {
2024-01-20 18:27:31 +03:00
if user . CustomAvatarRelativePath ( ) == "" {
return nil
}
2020-10-14 16:07:51 +03:00
_ , err := storage . Copy ( dstStorage , user . CustomAvatarRelativePath ( ) , storage . Avatars , user . CustomAvatarRelativePath ( ) )
return err
} )
}
2022-08-16 07:05:15 +03:00
func migrateRepoAvatars ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
2022-10-31 18:51:14 +03:00
return db . Iterate ( ctx , nil , func ( ctx context . Context , repo * repo_model . Repository ) error {
2024-01-20 18:27:31 +03:00
if repo . CustomAvatarRelativePath ( ) == "" {
return nil
}
2020-10-14 16:07:51 +03:00
_ , err := storage . Copy ( dstStorage , repo . CustomAvatarRelativePath ( ) , storage . RepoAvatars , repo . CustomAvatarRelativePath ( ) )
return err
} )
}
2022-08-16 07:05:15 +03:00
func migrateRepoArchivers ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
2022-10-31 18:51:14 +03:00
return db . Iterate ( ctx , nil , func ( ctx context . Context , archiver * repo_model . RepoArchiver ) error {
2022-08-29 12:45:20 +03:00
p := archiver . RelativePath ( )
_ , err := storage . Copy ( dstStorage , p , storage . RepoArchives , p )
2022-08-16 07:05:15 +03:00
return err
} )
}
func migratePackages ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
2022-10-31 18:51:14 +03:00
return db . Iterate ( ctx , nil , func ( ctx context . Context , pb * packages_model . PackageBlob ) error {
2022-08-16 07:05:15 +03:00
p := packages_module . KeyToRelativePath ( packages_module . BlobHash256Key ( pb . HashSHA256 ) )
_ , err := storage . Copy ( dstStorage , p , storage . Packages , p )
return err
} )
}
2023-05-12 16:30:28 +03:00
func migrateActionsLog ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
return db . Iterate ( ctx , nil , func ( ctx context . Context , task * actions_model . ActionTask ) error {
if task . LogExpired {
// the log has been cleared
return nil
}
if ! task . LogInStorage {
// running tasks store logs in DBFS
return nil
}
p := task . LogFilename
_ , err := storage . Copy ( dstStorage , p , storage . Actions , p )
return err
} )
}
2024-05-07 09:45:30 +03:00
func migrateActionsArtifacts ( ctx context . Context , dstStorage storage . ObjectStorage ) error {
return db . Iterate ( ctx , nil , func ( ctx context . Context , artifact * actions_model . ActionArtifact ) error {
2024-06-05 07:00:56 +03:00
if artifact . Status == int64 ( actions_model . ArtifactStatusExpired ) {
return nil
}
_ , err := storage . Copy ( dstStorage , artifact . StoragePath , storage . ActionsArtifacts , artifact . StoragePath )
if err != nil {
// ignore files that do not exist
if errors . Is ( err , fs . ErrNotExist ) {
return nil
}
return err
}
return nil
2024-05-07 09:45:30 +03:00
} )
}
2020-08-18 07:23:45 +03:00
func runMigrateStorage ( ctx * cli . Context ) error {
2021-11-07 06:11:27 +03:00
stdCtx , cancel := installSignals ( )
defer cancel ( )
if err := initDB ( stdCtx ) ; err != nil {
2020-08-18 07:23:45 +03:00
return err
}
2021-06-27 03:56:58 +03:00
log . Info ( "AppPath: %s" , setting . AppPath )
log . Info ( "AppWorkPath: %s" , setting . AppWorkPath )
log . Info ( "Custom path: %s" , setting . CustomPath )
2023-02-19 19:12:01 +03:00
log . Info ( "Log path: %s" , setting . Log . RootPath )
2021-09-14 04:24:57 +03:00
log . Info ( "Configuration file: %s" , setting . CustomConf )
2020-08-18 07:23:45 +03:00
2021-10-30 17:32:11 +03:00
if err := db . InitEngineWithMigration ( context . Background ( ) , migrations . Migrate ) ; err != nil {
2020-08-18 07:23:45 +03:00
log . Fatal ( "Failed to initialize ORM engine: %v" , err )
return err
}
if err := storage . Init ( ) ; err != nil {
return err
}
2020-09-08 18:45:10 +03:00
var dstStorage storage . ObjectStorage
var err error
2020-09-29 12:05:13 +03:00
switch strings . ToLower ( ctx . String ( "storage" ) ) {
2020-10-13 06:58:34 +03:00
case "" :
fallthrough
2023-06-14 06:42:38 +03:00
case string ( setting . LocalStorageType ) :
2020-09-08 18:45:10 +03:00
p := ctx . String ( "path" )
if p == "" {
2023-09-11 12:30:18 +03:00
log . Fatal ( "Path must be given when storage is local" )
2020-09-08 18:45:10 +03:00
return nil
}
2020-10-13 06:58:34 +03:00
dstStorage , err = storage . NewLocalStorage (
2022-08-16 07:05:15 +03:00
stdCtx ,
2023-06-14 06:42:38 +03:00
& setting . Storage {
2020-10-13 06:58:34 +03:00
Path : p ,
} )
2023-06-14 06:42:38 +03:00
case string ( setting . MinioStorageType ) :
2020-09-08 18:45:10 +03:00
dstStorage , err = storage . NewMinioStorage (
2022-08-16 07:05:15 +03:00
stdCtx ,
2023-06-14 06:42:38 +03:00
& setting . Storage {
MinioConfig : setting . MinioStorageConfig {
Endpoint : ctx . String ( "minio-endpoint" ) ,
AccessKeyID : ctx . String ( "minio-access-key-id" ) ,
SecretAccessKey : ctx . String ( "minio-secret-access-key" ) ,
Bucket : ctx . String ( "minio-bucket" ) ,
Location : ctx . String ( "minio-location" ) ,
BasePath : ctx . String ( "minio-base-path" ) ,
UseSSL : ctx . Bool ( "minio-use-ssl" ) ,
InsecureSkipVerify : ctx . Bool ( "minio-insecure-skip-verify" ) ,
ChecksumAlgorithm : ctx . String ( "minio-checksum-algorithm" ) ,
} ,
2020-10-13 06:58:34 +03:00
} )
2020-09-08 18:45:10 +03:00
default :
2022-08-16 07:05:15 +03:00
return fmt . Errorf ( "unsupported storage type: %s" , ctx . String ( "storage" ) )
2020-09-08 18:45:10 +03:00
}
if err != nil {
return err
}
2022-08-16 07:05:15 +03:00
migratedMethods := map [ string ] func ( context . Context , storage . ObjectStorage ) error {
2024-05-07 09:45:30 +03:00
"attachments" : migrateAttachments ,
"lfs" : migrateLFS ,
"avatars" : migrateAvatars ,
"repo-avatars" : migrateRepoAvatars ,
"repo-archivers" : migrateRepoArchivers ,
"packages" : migratePackages ,
"actions-log" : migrateActionsLog ,
"actions-artifacts" : migrateActionsArtifacts ,
2022-08-16 07:05:15 +03:00
}
2020-09-29 12:05:13 +03:00
tp := strings . ToLower ( ctx . String ( "type" ) )
2022-08-16 07:05:15 +03:00
if m , ok := migratedMethods [ tp ] ; ok {
if err := m ( stdCtx , dstStorage ) ; err != nil {
2020-10-14 16:07:51 +03:00
return err
}
2022-08-16 07:05:15 +03:00
log . Info ( "%s files have successfully been copied to the new storage." , tp )
return nil
2020-08-18 07:23:45 +03:00
}
2022-08-16 07:05:15 +03:00
return fmt . Errorf ( "unsupported storage: %s" , ctx . String ( "type" ) )
2020-08-18 07:23:45 +03:00
}