summaryrefslogtreecommitdiff
path: root/db/migrate/20161207231626_add_environment_slug.rb
blob: 8e98ee5b9ba18fd3488fe2bf4e1d256637e971b2 (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
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

class AddEnvironmentSlug < ActiveRecord::Migration
  include Gitlab::Database::MigrationHelpers

  DOWNTIME = true
  DOWNTIME_REASON = 'Adding NOT NULL column environments.slug with dependent data'

  # Used to generate random suffixes for the slug
  LETTERS = 'a'..'z'
  NUMBERS = '0'..'9'
  SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a

  def up
    environments = Arel::Table.new(:environments)

    add_column :environments, :slug, :string
    finder = environments.project(:id, :name)

    connection.exec_query(finder.to_sql).rows.each do |id, name|
      updater = Arel::UpdateManager.new(ActiveRecord::Base).
        table(environments).
        set(environments[:slug] => generate_slug(name)).
        where(environments[:id].eq(id))

      connection.exec_update(updater.to_sql, self.class.name, [])
    end

    change_column_null :environments, :slug, false
  end

  def down
    remove_column :environments, :slug
  end

  # Copy of the Environment#generate_slug implementation
  def generate_slug(name)
    # Lowercase letters and numbers only
    slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-')

    # Must start with a letter
    slugified = 'env-' + slugified unless LETTERS.cover?(slugified[0])

    # Repeated dashes are invalid (OpenShift limitation)
    slugified.gsub!(/\-+/, '-')

    # Maximum length: 24 characters (OpenShift limitation)
    slugified = slugified[0..23]

    # Cannot end with a dash (Kubernetes label limitation)
    slugified.chop! if slugified.end_with?('-')

    # Add a random suffix, shortening the current string if necessary, if it
    # has been slugified. This ensures uniqueness.
    if slugified != name
      slugified = slugified[0..16]
      slugified << '-' unless slugified.end_with?('-')
      slugified << random_suffix
    end

    slugified
  end

  def random_suffix
    (0..5).map { SUFFIX_CHARS.sample }.join
  end
end