summaryrefslogtreecommitdiff
path: root/db/post_migrate/20191220102807_patch_prometheus_services_for_shared_cluster_applications.rb
blob: 68361f7b176b00e170ca1f5b0fd4548877ebb2a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
57
58
59
60
61
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
# frozen_string_literal: true

class PatchPrometheusServicesForSharedClusterApplications < ActiveRecord::Migration[5.2]
  include Gitlab::Database::MigrationHelpers

  DOWNTIME = false
  MIGRATION = 'ActivatePrometheusServicesForSharedClusterApplications'.freeze
  BATCH_SIZE = 500
  DELAY = 2.minutes

  disable_ddl_transaction!

  module Migratable
    module Applications
      class Prometheus < ActiveRecord::Base
        self.table_name = 'clusters_applications_prometheus'

        enum status: {
          errored: -1,
          installed: 3,
          updated: 5
        }
      end
    end

    class Project < ActiveRecord::Base
      self.table_name = 'projects'
      include ::EachBatch

      scope :with_application_on_group_clusters, -> {
        joins("INNER JOIN namespaces ON namespaces.id = projects.namespace_id")
          .joins("INNER JOIN cluster_groups ON cluster_groups.group_id = namespaces.id")
          .joins("INNER JOIN clusters ON clusters.id = cluster_groups.cluster_id AND clusters.cluster_type = #{Cluster.cluster_types['group_type']}")
          .joins("INNER JOIN clusters_applications_prometheus ON clusters_applications_prometheus.cluster_id = clusters.id
                      AND clusters_applications_prometheus.status IN (#{Applications::Prometheus.statuses[:installed]}, #{Applications::Prometheus.statuses[:updated]})")
      }

      scope :without_active_prometheus_services, -> {
        joins("LEFT JOIN services ON services.project_id = projects.id AND services.type = 'PrometheusService'")
          .where("services.id IS NULL OR (services.active = FALSE AND services.properties = '{}')")
      }
    end

    class Cluster < ActiveRecord::Base
      self.table_name = 'clusters'

      enum cluster_type: {
        instance_type: 1,
        group_type: 2
      }

      def self.has_prometheus_application?
        joins("INNER JOIN clusters_applications_prometheus ON clusters_applications_prometheus.cluster_id = clusters.id
                   AND clusters_applications_prometheus.status IN (#{Applications::Prometheus.statuses[:installed]}, #{Applications::Prometheus.statuses[:updated]})").exists?
      end
    end
  end

  def up
    projects_without_active_prometheus_service.group('projects.id').each_batch(of: BATCH_SIZE) do |batch, index|
      bg_migrations_batch = batch.select('projects.id').map { |project| [MIGRATION, project.id] }
      delay = index * DELAY
      BackgroundMigrationWorker.bulk_perform_in(delay.seconds, bg_migrations_batch)
    end
  end

  def down
    # no-op
  end

  private

  def projects_without_active_prometheus_service
    scope = Migratable::Project.without_active_prometheus_services

    return scope if migrate_instance_cluster?

    scope.with_application_on_group_clusters
  end

  def migrate_instance_cluster?
    if instance_variable_defined?('@migrate_instance_cluster')
      @migrate_instance_cluster
    else
      @migrate_instance_cluster = Migratable::Cluster.instance_type.has_prometheus_application?
    end
  end
end