FarmBot/Farmbot-Web-App

View on GitHub
app/lib/rmq_config_writer.rb

Summary

Maintainability
A
0 mins
Test Coverage
# SCENARIO: RabbitMQ's HTTP auth backend plugin uses *files* instead of ENV vars
#           for configuration.
# PROBLEM: 1. This is the only service that requires such configuration. All
#             other services are configured in one place- the `.env` file.
#          2. When using files, it's easy to forget that the file needs updates.
#             Refreshing the .env file will NOT refresh rabbit configs.
#          3. This adds an additional step to server provisioning procedures.
# SOLUTION: Since Rabbit requires the API to load ahead of itself, we can create
#           the configuration on-the-fly when the API boots up by using custom
#           ENV vars and string templates. Essentially, the API overwrites the
#           contents of rabbitmq.conf every time it boots up, eliminating the
#           need to refresh/create cumbersome config files.
class RmqConfigWriter
  CONFIG_PATH = "docker_volumes/rabbit"
  CONFIG_FILENAME = "farmbot_rmq_config.conf"
  CONFIG_OUTPUT = "#{CONFIG_PATH}/#{CONFIG_FILENAME}"
  NO_API_HOST = "\nYou MUST set API_HOST to a real IP address or " +
                "domain name (not localhost).\n" +
                "API_PORT is also mandatory."
  PROTO = ENV["FORCE_SSL"] ? "https:" : "http:"
  ADMIN_PASSWORD = ENV["ADMIN_PASSWORD"]
  CFG_DATA = { admin_password: ADMIN_PASSWORD,
               fully_formed_url: PROTO + ($API_URL || "") }
  ENV_WARNING = "Warning: ENV[\"ADMIN_PASSWORD\"] not set. If you are not " \
                "using 3rd party MQTT hosting, please set this value and " \
                "re-build the server image."
  BAD_PASSWORD = "Password is not strong enough. Use more characters"
  TEMPLATE = <<~END
    # THIS FILE WAS GENERATED BY THE `RmqConfigWriter` CLASS IN THE RAILS APP
    # == DO NOT ATTEMPT TO MODIFY MANUALLY - IT WILL BE OVERWRITTEN ===
    auth_backends.1                 = cache
    auth_cache.cache_ttl            = 600000
    auth_cache.cached_backend       = http
    auth_http.http_method           = post
    auth_http.resource_path         = %{fully_formed_url}/api/rmq/resource
    auth_http.topic_path            = %{fully_formed_url}/api/rmq/topic
    auth_http.user_path             = %{fully_formed_url}/api/rmq/user
    auth_http.vhost_path            = %{fully_formed_url}/api/rmq/vhost
    default_user                    = admin
    default_user_tags.administrator = true
    default_user_tags.management    = true
    default_pass                    = %{admin_password}
    mqtt.allow_anonymous            = false
  END

  def self.do_render
    raise BAD_PASSWORD if ADMIN_PASSWORD.length < 5
    FileUtils.mkdir_p CONFIG_PATH
    File.open(CONFIG_OUTPUT, "w+") { |f| f.write(TEMPLATE % CFG_DATA) }
  end

  def self.dont_render
    puts ENV_WARNING
  end

  def self.env_ok?
    ADMIN_PASSWORD && ENV.key?("API_HOST") && ENV.key?("API_PORT")
  end

  def self.render
    env_ok? ? do_render : dont_render
  end
end