gitea源码

migrate_storage.go 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cmd
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "io/fs"
  9. "strings"
  10. actions_model "code.gitea.io/gitea/models/actions"
  11. "code.gitea.io/gitea/models/db"
  12. git_model "code.gitea.io/gitea/models/git"
  13. packages_model "code.gitea.io/gitea/models/packages"
  14. repo_model "code.gitea.io/gitea/models/repo"
  15. user_model "code.gitea.io/gitea/models/user"
  16. "code.gitea.io/gitea/modules/log"
  17. packages_module "code.gitea.io/gitea/modules/packages"
  18. "code.gitea.io/gitea/modules/setting"
  19. "code.gitea.io/gitea/modules/storage"
  20. "code.gitea.io/gitea/services/versioned_migration"
  21. "github.com/urfave/cli/v3"
  22. )
  23. // CmdMigrateStorage represents the available migrate storage sub-command.
  24. var CmdMigrateStorage = &cli.Command{
  25. Name: "migrate-storage",
  26. Usage: "Migrate the storage",
  27. Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
  28. Action: runMigrateStorage,
  29. Flags: []cli.Flag{
  30. &cli.StringFlag{
  31. Name: "type",
  32. Aliases: []string{"t"},
  33. Value: "",
  34. Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts",
  35. },
  36. &cli.StringFlag{
  37. Name: "storage",
  38. Aliases: []string{"s"},
  39. Value: "",
  40. Usage: "New storage type: local (default), minio or azureblob",
  41. },
  42. &cli.StringFlag{
  43. Name: "path",
  44. Aliases: []string{"p"},
  45. Value: "",
  46. Usage: "New storage placement if store is local (leave blank for default)",
  47. },
  48. // Minio Storage special configurations
  49. &cli.StringFlag{
  50. Name: "minio-endpoint",
  51. Value: "",
  52. Usage: "Minio storage endpoint",
  53. },
  54. &cli.StringFlag{
  55. Name: "minio-access-key-id",
  56. Value: "",
  57. Usage: "Minio storage accessKeyID",
  58. },
  59. &cli.StringFlag{
  60. Name: "minio-secret-access-key",
  61. Value: "",
  62. Usage: "Minio storage secretAccessKey",
  63. },
  64. &cli.StringFlag{
  65. Name: "minio-bucket",
  66. Value: "",
  67. Usage: "Minio storage bucket",
  68. },
  69. &cli.StringFlag{
  70. Name: "minio-location",
  71. Value: "",
  72. Usage: "Minio storage location to create bucket",
  73. },
  74. &cli.StringFlag{
  75. Name: "minio-base-path",
  76. Value: "",
  77. Usage: "Minio storage base path on the bucket",
  78. },
  79. &cli.BoolFlag{
  80. Name: "minio-use-ssl",
  81. Usage: "Enable SSL for minio",
  82. },
  83. &cli.BoolFlag{
  84. Name: "minio-insecure-skip-verify",
  85. Usage: "Skip SSL verification",
  86. },
  87. &cli.StringFlag{
  88. Name: "minio-checksum-algorithm",
  89. Value: "",
  90. Usage: "Minio checksum algorithm (default/md5)",
  91. },
  92. &cli.StringFlag{
  93. Name: "minio-bucket-lookup-type",
  94. Value: "",
  95. Usage: "Minio bucket lookup type",
  96. },
  97. // Azure Blob Storage special configurations
  98. &cli.StringFlag{
  99. Name: "azureblob-endpoint",
  100. Value: "",
  101. Usage: "Azure Blob storage endpoint",
  102. },
  103. &cli.StringFlag{
  104. Name: "azureblob-account-name",
  105. Value: "",
  106. Usage: "Azure Blob storage account name",
  107. },
  108. &cli.StringFlag{
  109. Name: "azureblob-account-key",
  110. Value: "",
  111. Usage: "Azure Blob storage account key",
  112. },
  113. &cli.StringFlag{
  114. Name: "azureblob-container",
  115. Value: "",
  116. Usage: "Azure Blob storage container",
  117. },
  118. &cli.StringFlag{
  119. Name: "azureblob-base-path",
  120. Value: "",
  121. Usage: "Azure Blob storage base path",
  122. },
  123. },
  124. }
  125. func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error {
  126. return db.Iterate(ctx, nil, func(ctx context.Context, attach *repo_model.Attachment) error {
  127. _, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath())
  128. return err
  129. })
  130. }
  131. func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error {
  132. return db.Iterate(ctx, nil, func(ctx context.Context, mo *git_model.LFSMetaObject) error {
  133. _, err := storage.Copy(dstStorage, mo.RelativePath(), storage.LFS, mo.RelativePath())
  134. return err
  135. })
  136. }
  137. func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
  138. return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
  139. if user.CustomAvatarRelativePath() == "" {
  140. return nil
  141. }
  142. _, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
  143. return err
  144. })
  145. }
  146. func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
  147. return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
  148. if repo.CustomAvatarRelativePath() == "" {
  149. return nil
  150. }
  151. _, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
  152. return err
  153. })
  154. }
  155. func migrateRepoArchivers(ctx context.Context, dstStorage storage.ObjectStorage) error {
  156. return db.Iterate(ctx, nil, func(ctx context.Context, archiver *repo_model.RepoArchiver) error {
  157. p := archiver.RelativePath()
  158. _, err := storage.Copy(dstStorage, p, storage.RepoArchives, p)
  159. return err
  160. })
  161. }
  162. func migratePackages(ctx context.Context, dstStorage storage.ObjectStorage) error {
  163. return db.Iterate(ctx, nil, func(ctx context.Context, pb *packages_model.PackageBlob) error {
  164. p := packages_module.KeyToRelativePath(packages_module.BlobHash256Key(pb.HashSHA256))
  165. _, err := storage.Copy(dstStorage, p, storage.Packages, p)
  166. return err
  167. })
  168. }
  169. func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) error {
  170. return db.Iterate(ctx, nil, func(ctx context.Context, task *actions_model.ActionTask) error {
  171. if task.LogExpired {
  172. // the log has been cleared
  173. return nil
  174. }
  175. if !task.LogInStorage {
  176. // running tasks store logs in DBFS
  177. return nil
  178. }
  179. p := task.LogFilename
  180. _, err := storage.Copy(dstStorage, p, storage.Actions, p)
  181. return err
  182. })
  183. }
  184. func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
  185. return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
  186. if artifact.Status == actions_model.ArtifactStatusExpired {
  187. return nil
  188. }
  189. _, err := storage.Copy(dstStorage, artifact.StoragePath, storage.ActionsArtifacts, artifact.StoragePath)
  190. if err != nil {
  191. // ignore files that do not exist
  192. if errors.Is(err, fs.ErrNotExist) {
  193. return nil
  194. }
  195. return err
  196. }
  197. return nil
  198. })
  199. }
  200. func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
  201. if err := initDB(ctx); err != nil {
  202. return err
  203. }
  204. log.Info("AppPath: %s", setting.AppPath)
  205. log.Info("AppWorkPath: %s", setting.AppWorkPath)
  206. log.Info("Custom path: %s", setting.CustomPath)
  207. log.Info("Log path: %s", setting.Log.RootPath)
  208. log.Info("Configuration file: %s", setting.CustomConf)
  209. if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
  210. log.Fatal("Failed to initialize ORM engine: %v", err)
  211. return err
  212. }
  213. if err := storage.Init(); err != nil {
  214. return err
  215. }
  216. var dstStorage storage.ObjectStorage
  217. var err error
  218. switch strings.ToLower(cmd.String("storage")) {
  219. case "":
  220. fallthrough
  221. case string(setting.LocalStorageType):
  222. p := cmd.String("path")
  223. if p == "" {
  224. log.Fatal("Path must be given when storage is local")
  225. return nil
  226. }
  227. dstStorage, err = storage.NewLocalStorage(
  228. ctx,
  229. &setting.Storage{
  230. Path: p,
  231. })
  232. case string(setting.MinioStorageType):
  233. dstStorage, err = storage.NewMinioStorage(
  234. ctx,
  235. &setting.Storage{
  236. MinioConfig: setting.MinioStorageConfig{
  237. Endpoint: cmd.String("minio-endpoint"),
  238. AccessKeyID: cmd.String("minio-access-key-id"),
  239. SecretAccessKey: cmd.String("minio-secret-access-key"),
  240. Bucket: cmd.String("minio-bucket"),
  241. Location: cmd.String("minio-location"),
  242. BasePath: cmd.String("minio-base-path"),
  243. UseSSL: cmd.Bool("minio-use-ssl"),
  244. InsecureSkipVerify: cmd.Bool("minio-insecure-skip-verify"),
  245. ChecksumAlgorithm: cmd.String("minio-checksum-algorithm"),
  246. BucketLookUpType: cmd.String("minio-bucket-lookup-type"),
  247. },
  248. })
  249. case string(setting.AzureBlobStorageType):
  250. dstStorage, err = storage.NewAzureBlobStorage(
  251. ctx,
  252. &setting.Storage{
  253. AzureBlobConfig: setting.AzureBlobStorageConfig{
  254. Endpoint: cmd.String("azureblob-endpoint"),
  255. AccountName: cmd.String("azureblob-account-name"),
  256. AccountKey: cmd.String("azureblob-account-key"),
  257. Container: cmd.String("azureblob-container"),
  258. BasePath: cmd.String("azureblob-base-path"),
  259. },
  260. })
  261. default:
  262. return fmt.Errorf("unsupported storage type: %s", cmd.String("storage"))
  263. }
  264. if err != nil {
  265. return err
  266. }
  267. migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
  268. "attachments": migrateAttachments,
  269. "lfs": migrateLFS,
  270. "avatars": migrateAvatars,
  271. "repo-avatars": migrateRepoAvatars,
  272. "repo-archivers": migrateRepoArchivers,
  273. "packages": migratePackages,
  274. "actions-log": migrateActionsLog,
  275. "actions-artifacts": migrateActionsArtifacts,
  276. }
  277. tp := strings.ToLower(cmd.String("type"))
  278. if m, ok := migratedMethods[tp]; ok {
  279. if err := m(ctx, dstStorage); err != nil {
  280. return err
  281. }
  282. log.Info("%s files have successfully been copied to the new storage.", tp)
  283. return nil
  284. }
  285. return fmt.Errorf("unsupported storage: %s", cmd.String("type"))
  286. }