configure_environment.rb 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #!/usr/bin/env ruby
  2. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  3. require 'yaml'
  4. require 'resolv'
  5. require 'fileutils'
  6. #
  7. # Configures the CI system
  8. # - either (randomly) mysql or postgresql, if it is available
  9. # - (randomly) Redis or File as web socket session back end, if Redis is available
  10. #
  11. # Database config happens directly in config/database.yml, other settings are written to
  12. # .gitlab/environment.env which must be sourced in the CI configuration.
  13. #
  14. class ConfigureEnvironment
  15. @env_file_content = <<~ENV_FILE_CONTENT
  16. #!/bin/bash
  17. FRESHENVFILE=fresh.env && test -f $FRESHENVFILE && source $FRESHENVFILE
  18. true
  19. ENV_FILE_CONTENT
  20. def self.configure_database # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
  21. if File.exist? File.join(__dir__, '../config/database.yml')
  22. puts "'config/database.yml' already exists and will not be changed."
  23. return
  24. end
  25. cnf = YAML.load_file(File.join(__dir__, '../config/database/database.yml'))
  26. cnf.delete('default')
  27. database = ENV['ENFORCE_DB_SERVICE']
  28. # Lookup in /etc/hosts first: gitlab uses that if FF_NETWORK_PER_BUILD is not set.
  29. if !database
  30. hostsfile = '/etc/hosts'
  31. database = %w[postgresql mysql].shuffle.find do |possible_database|
  32. File.foreach(hostsfile).any? { |l| l[possible_database] }
  33. end
  34. end
  35. # Lookup via DNS if needed: gitlab uses that if FF_NETWORK_PER_BUILD is enabled.
  36. if !database
  37. dns = Resolv::DNS.new
  38. dns.timeouts = 3
  39. database = %w[postgresql mysql].shuffle.find do |possible_database|
  40. # Perform a lookup of the database host to check if it is configured as a service.
  41. if dns.getaddress possible_database
  42. next possible_database
  43. end
  44. rescue Resolv::ResolvError
  45. # Ignore DNS lookup errors
  46. end
  47. end
  48. raise "Can't find any supported database." if database.nil?
  49. puts "Using #{database} as database service."
  50. db_settings_map = {
  51. 'postgresql' => {
  52. 'adapter' => 'postgresql',
  53. 'username' => 'zammad',
  54. 'password' => 'zammad',
  55. 'host' => 'postgresql', # db alias from gitlab-ci.yml
  56. },
  57. 'mysql' => {
  58. 'adapter' => 'mysql2',
  59. 'username' => 'root',
  60. 'password' => 'zammad',
  61. 'host' => 'mysql', # db alias from gitlab-ci.yml
  62. }
  63. }
  64. # fetch DB settings from settings map and fallback to postgresql
  65. db_settings = db_settings_map.fetch(database) { db_settings_map['postgresql'] }
  66. %w[development test production].each do |environment|
  67. cnf[environment].merge!(db_settings)
  68. end
  69. File.write(File.join(__dir__, '../config/database.yml'), Psych.dump(cnf))
  70. end
  71. def self.configure_redis # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
  72. puts 'ENABLING THE NEW EXPERIMENTAL MOBILE FRONTEND.' if ENV['ENABLE_EXPERIMENTAL_MOBILE_FRONTEND'] == 'true'
  73. if ENV['ENABLE_EXPERIMENTAL_MOBILE_FRONTEND'] == 'true' && (ENV['REDIS_URL'].nil? || ENV['REDIS_URL'].empty?) # rubocop:disable Rails/Blank
  74. if database_type == 'mysql'
  75. raise 'Redis was not found, but is required for ActionCable on MySQL based systems.'
  76. end
  77. puts 'Redis is not available, using File as web socket session store.'
  78. puts 'Redis is not available, using the PostgreSQL adapter for ActionCable.'
  79. return
  80. end
  81. if database_type == 'mysql' || [true, false].sample
  82. puts 'Using Redis as web socket session store.'
  83. puts 'Using the Redis adapter for ActionCable.'
  84. return
  85. end
  86. puts 'Using File as web socket session store.'
  87. puts 'Using the PostgreSQL adapter for ActionCable.'
  88. @env_file_content += "unset REDIS_URL\n"
  89. end
  90. def self.configure_memcached
  91. if ENV['MEMCACHE_SERVERS'].nil? || ENV['MEMCACHE_SERVERS'].empty? # rubocop:disable Rails/Blank
  92. puts 'Memcached is not available, using File as Rails cache store.'
  93. return
  94. end
  95. if [true, false].sample
  96. puts 'Using memcached as Rails cache store.'
  97. return
  98. end
  99. puts "Using Zammad's file store as Rails cache store."
  100. @env_file_content += "unset MEMCACHE_SERVERS\n"
  101. end
  102. # Since configure_database skips if database.yml already exists, check the
  103. # content of that file to reliably determine the database type in all cases.
  104. def self.database_type
  105. database = File.read(File.join(__dir__, '../config/database.yml')).match(%r{^\s*adapter:\s*(mysql|postgresql)})[1]
  106. if !database
  107. raise 'Could not determine database type, cannot setup cable.yml'
  108. end
  109. database
  110. end
  111. def self.write_env_file
  112. File.write(File.join(__dir__, 'environment.env'), @env_file_content)
  113. end
  114. def self.run
  115. configure_database
  116. configure_redis
  117. configure_memcached
  118. write_env_file
  119. end
  120. end
  121. ConfigureEnvironment.run