Class: FileStore::ToS3Migration

Inherits:
Object
  • Object
show all
Defined in:
lib/file_store/to_s3_migration.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(s3_options:, dry_run: false, migrate_to_multisite: false) ⇒ ToS3Migration

Returns a new instance of ToS3Migration.



12
13
14
15
16
17
18
# File 'lib/file_store/to_s3_migration.rb', line 12

def initialize(s3_options:, dry_run: false, migrate_to_multisite: false)
  @s3_bucket = s3_options[:bucket]
  @s3_client_options = s3_options[:client_options]
  @dry_run = dry_run
  @migrate_to_multisite = migrate_to_multisite
  @current_db = RailsMultisite::ConnectionManagement.current_db
end

Class Method Details

.s3_options_from_envObject



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/file_store/to_s3_migration.rb', line 27

def self.s3_options_from_env
  if ENV["DISCOURSE_S3_BUCKET"].blank? || ENV["DISCOURSE_S3_REGION"].blank? ||
       !(
         (
           ENV["DISCOURSE_S3_ACCESS_KEY_ID"].present? &&
             ENV["DISCOURSE_S3_SECRET_ACCESS_KEY"].present?
         ) || ENV["DISCOURSE_S3_USE_IAM_PROFILE"].present?
       )
    raise ToS3MigrationError.new(<<~TEXT)
      Please provide the following environment variables:
        - DISCOURSE_S3_BUCKET
        - DISCOURSE_S3_REGION
        and either
        - DISCOURSE_S3_ACCESS_KEY_ID
        - DISCOURSE_S3_SECRET_ACCESS_KEY
        or
        - DISCOURSE_S3_USE_IAM_PROFILE
    TEXT
  end

  opts = { region: ENV["DISCOURSE_S3_REGION"] }
  opts[:endpoint] = ENV["DISCOURSE_S3_ENDPOINT"] if ENV["DISCOURSE_S3_ENDPOINT"].present?

  if ENV["DISCOURSE_S3_USE_IAM_PROFILE"].blank?
    opts[:access_key_id] = ENV["DISCOURSE_S3_ACCESS_KEY_ID"]
    opts[:secret_access_key] = ENV["DISCOURSE_S3_SECRET_ACCESS_KEY"]
  end

  { client_options: opts, bucket: ENV["DISCOURSE_S3_BUCKET"] }
end

.s3_options_from_site_settingsObject



20
21
22
23
24
25
# File 'lib/file_store/to_s3_migration.rb', line 20

def self.s3_options_from_site_settings
  {
    client_options: S3Helper.s3_options(SiteSetting),
    bucket: SiteSetting.Upload.s3_upload_bucket,
  }
end

Instance Method Details

#migrateObject



58
59
60
# File 'lib/file_store/to_s3_migration.rb', line 58

def migrate
  migrate_to_s3
end

#migration_successful?(should_raise: false) ⇒ Boolean

Returns:

  • (Boolean)


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/file_store/to_s3_migration.rb', line 62

def migration_successful?(should_raise: false)
  success = true

  failure_message = "S3 migration failed for db '#{@current_db}'."
  prefix = @migrate_to_multisite ? "uploads/#{@current_db}/original/" : "original/"

  base_url = File.join(SiteSetting.Upload.s3_base_url, prefix)
  count = Upload.by_users.where("url NOT LIKE '#{base_url}%'").count
  if count > 0
    error_message =
      "#{count} of #{Upload.count} uploads are not migrated to S3. #{failure_message}"
    raise_or_log(error_message, should_raise)
    success = false
  end

  cdn_path = SiteSetting.cdn_path("/uploads/#{@current_db}/original").sub(/https?:/, "")
  count = Post.where("cooked LIKE '%#{cdn_path}%'").count
  if count > 0
    error_message = "#{count} posts are not remapped to new S3 upload URL. #{failure_message}"
    raise_or_log(error_message, should_raise)
    success = false
  end

  unless Rake::Task.task_defined?(MISSING_UPLOADS_RAKE_TASK_NAME)
    Discourse::Application.load_tasks
  end
  Rake::Task[MISSING_UPLOADS_RAKE_TASK_NAME]
  count = DB.query_single(<<~SQL, Post::MISSING_UPLOADS, Post::MISSING_UPLOADS_IGNORED).first
    SELECT COUNT(1)
    FROM posts p
    WHERE EXISTS (
      SELECT 1
      FROM post_custom_fields f
      WHERE f.post_id = p.id AND f.name = ?
    ) AND NOT EXISTS (
      SELECT 1
      FROM post_custom_fields f
      WHERE f.post_id = p.id AND f.name = ?
    )
  SQL
  if count > 0
    error_message = "rake posts:missing_uploads identified #{count} issues. #{failure_message}"
    raise_or_log(error_message, should_raise)
    success = false
  end

  count = Post.where("baked_version <> ? OR baked_version IS NULL", Post::BAKED_VERSION).count
  if count > 0
    log("#{count} posts still require rebaking and will be rebaked during regular job")
    if count > 100
      log(
        "To speed up migrations of posts we recommend you run 'rake posts:rebake_uncooked_posts'",
      )
    end
    success = false
  else
    log("No posts require rebaking")
  end

  success
end