require 'ostruct' require 'yaml' require 'cloud/settings' # A Rack middleware application that creates a single configuration for Helix Web # Services. # # Most web services are modular Sinatra applications, which does come with it's # own settings mechanism. We should try to avoid those settings in most cases. # Perhaps only if those settings are only relevant to the logic directly # within the Sinatra app. # # Many other settings, like the port setting of the associated Helix Versioning # Engine, should be exposed and overridable by the client application. These # settings should be defined here. # # This class provides middleware that will inject an `hws_settings` object into # each request. This `hws_settings` object is seeded by values declared on this # class. When the system starts, our system config file is read in, and default # system values are overridden. On any request, these settings can be # overridden by the user. # # Additionally, there are "system" settings that are only overridable from the # system config file. Client classes should reference this class directly: # HWSSettings.system. # # ## Naming Conventions # # Use uppercase letters, numbers, or underscores only. # # ## HTTP Header Override Syntax # # We allow per-request overrides of settings via HTTP headers. # # The key format of the custom setting is: # # `X-PERFORCE-HELIX_WEB_SERVICES-{key}` # # For example: # # X-PERFORCE-HELIX_WEB_SERVICES-P4HOST: perforce.mycompany.com # X-PERFORCE-HELIX_WEB_SERVICES-P4CHARSET: auto # # Please note that headers will be converted by Rack to all uppercase, hence # our naming conventions. # # ## System Config File # # The system configuration is stored in the # `/etc/perforce/helix_web_services.conf` file. This is a YAML # file, and we override any locally defined variables with values found in # this file. ()If you specify a value in this file we do not locally define, # we ignore it.) # # Example values in /etc/perforce/helix_web_services.conf: # # P4HOST: 'perforce.mycompany.com' # P4PORT: '9991' # # class HWSSettings SYSTEM_CONFIG_PATH = ENV['HWS_CONFIG'] || '/etc/perforce/helix_web_services.conf' @settings = OpenStruct.new( # The .git-fusion depot name :GIT_FUSION_DEPOT => '.git-fusion', # If set, the HVEProjects class will use this path to locate projects # in the system. :HVE_PROJECTS_PATH => nil, # The current API level used to interact with the Perforce server :P4APILEVEL => '78', # The hostname of the p4d (Helix Versioning Engine) instance :P4HOST => nil, # The port of the p4d (Helix Versioning Engine) instance :P4PORT => '1666', # The charset setting to use when connecting to the p4d instance :P4CHARSET => 'auto', # The password or ticket to use when generating the p4 connection for the # method :P4PASSWD => nil ) @system = OpenStruct.new( # Allow the indicated commands to be used in the # /p4/v1/commands methods # Each entry is either a string (the command name) or an Array of command # and required arguments. :COMMAND_WHITELIST => ['info', ['files', '-m']], # We'll load up any ruby scripts after initializing most of our code # here, in case rules need to be added. :CUSTOM_SCRIPTS => '/var/lib/perforce/helix_web_services/scripts', :DATA_DIR => '/var/lib/perforce/helix_web_services', # Enable Git Fusion project helpers. :ENABLE_GIT_FUSION => false, # If you receive a 'p4 trust' failure, this will accept the trust from # the remote server and retry again. This is not recommended for # production servers. You should use TRUST_FINGERPRINTS instead :ENABLE_MAN_IN_MIDDLE_ATTACKS => false, # If you should use HTTPS at the nginx level or not :ENABLE_HTTPS => true, # The amount of time the Helix Sync submit reconciliation mechanism will # retry any reconciliation failures. :HELIX_SYNC_RECONCILE_RETRIES => 5, :HELIX_SYNC_LOCK_RETRIES => 30, # The directory location we direct all logs to :LOG_DIR => '/var/log/perforce/helix_web_services', # Path to the 'nginx' executable :NGINX_COMMAND => '/opt/perforce/helix-web-services/embedded/sbin/nginx', # The nginx configuration is reset on restart :NGINX_CONFIG_PATH => '/var/run/perforce/helix_web_services/nginx.conf', :NGINX_WORKER_CONNECTIONS => 128, # What port nginx should respond to :NGINX_PORT => 443, # The local file to use for p4trust (if ENV['P4TRUST'] isn't set) :P4TRUST => '/var/lib/perforce/helix_web_services/p4trust', :RACKUP_CONFIG => '/opt/perforce/helix-web-services/embedded/service/helix_web_services/config.ru', :RUN_DIR => '/var/run/perforce/helix_web_services', :SSL_CERTIFICATE_PATH => '/opt/perforce/helix-web-services/nginx.crt', :SSL_CERTIFICATE_KEY_PATH => '/opt/perforce/helix-web-services/nginx.key', # If this is set, this is the file location where trusted fingerprints # are stored, one per line. We will *only* accept the fingerprint in # this file. :TRUST_FINGERPRINTS => nil, # The local file location that defines where services are located :SERVICE_CONFIGURATION => '/etc/perforce/services.json', # This is the user account we run unicorn as, which should be set during # the installation process. :SYSTEM_GROUP => 'helixwebservices', :SYSTEM_USER => 'helixwebservices', # Use this working directory for temporary workspaces (typically for making submits) :WORKSPACE_DIR => '/var/lib/perforce/helix_web_services/workspaces', :UNICORN_COMMAND => 'unicorn', :UNICORN_CONFIG_PATH => '/var/run/perforce/helix_web_services/unicorn.conf', # Unicorn will use this path to communicate to nginx. :UNICORN_CONNECTION => 'unix:/var/run/perforce/helix_web_services/unicorn.sock', # Seconds until we kill the unicorn process. # This is pretty slow (5 minutes) to let Helix Sync submit work :UNICORN_TIMEOUT => 300, # The number of forks we run. :UNICORN_WORKER_PROCESSES => 8 ) class << self # Returns baseline settings with system overrides applied. # # This is a copy of state. If you want to alter the default settings in # code instead of via config files, use the `settings_handle` def settings s = OpenStruct.new(@settings) s.each_pair do |key, _| if overrides.respond_to?(key) s[key] = overrides[key] end end s end # You can tweak the default settings directly here in code. def settings_handle @settings end # Returns our system settings overridden by local configuration in overrides. # # This is a *copy* of the class system settings, suitable for editing and # passing on. # # See the official guide for declared options. def system s = OpenStruct.new(@system) s.each_pair do |key, _| if overrides.respond_to?(key) s[key] = overrides[key] end end s end # In case your code wants to edit the system classes directly. Typically # used for test initialization. def system_handle @system end # Return the system overrides in our system configuration file. def overrides return @overrides ||= init_overrides end private def init_overrides system_conf_path = ENV['SYSTEM_CONFIG_PATH'] || SYSTEM_CONFIG_PATH if File.exists?(system_conf_path) yaml = YAML.load_file(SYSTEM_CONFIG_PATH) OpenStruct.new(yaml) end end end def initialize(app) @app = app end def call(env) hws_settings = self.class.settings # TODO: need to whitelist P4PORT for cloud, arbitrary perforce servers not supported env.each do |key, value| match = /^HTTP_X_PERFORCE_HELIX_WEB_SERVICES_(.*)$/.match(key) if match hws_settings[match[1]] = value end end env['hws_settings'] = hws_settings @app.call(env) end end