require 'hws_settings' require 'p4_error' require 'uri' require 'rbconfig' module P4Util # Create a p4 connection using our Rack environment setup. # # This will seed options to a call to open() via attributes set on env: # # - `hws_settings`, which contains things like P4PORT # - `AUTH_CREDENTIALS`, which might be used if this is called after our Auth # middleware (or we've stored these credentials in a session, etc) def self.open_from_env(env) options = {} if env.key?('hws_settings') hws_settings = env['hws_settings'] port = nil if hws_settings.P4URL p4_url = URI(hws_settings.P4URL) options[:port] = "#{p4_url.host}:#{p4_url.port}" end if hws_settings.P4HOST options[:host] = hws_settings.P4HOST end if hws_settings.P4PORT # Apparently, the P4API will ports with 'rsh:' to mean "I'm going # to have a way to launch random things for you". It's great. And by # great I mean a another fantastic feature that makes no sense to anyone # who's not debugging a local client application. # # See netportparser.cc for how the P4API parses out the transport type. if hws_settings.P4PORT.downcase.start_with?('rsh:') || hws_settings.P4PORT.downcase.start_with?('jsh:') fail P4Error.new(0, 3, 'Do not use rsh: or jsh: P4PORT values') end options[:port] = hws_settings.P4PORT end if hws_settings.P4CHARSET options[:charset] = hws_settings.P4CHARSET end if hws_settings.P4APILEVEL options[:api_level] = Integer(hws_settings.P4APILEVEL) end if hws_settings.P4PASSWD options[:password] = hws_settings.P4PASSWD end end if env.key?('AUTH_CREDENTIALS') options[:user] = env['AUTH_CREDENTIALS'].first if !options.key?(:password) or options[:password].nil? options[:password] = env['AUTH_CREDENTIALS'].last end end options[:client] = 'INVALID' self.open(options) end # Creates your p4 connection using some common forms. # # If you call open with a block, this will call connect before your block # executes, and disconnect afterwards. # # If you do not call open with a block, it is up to the caller to connect # and disconnect. (It's assumed you are calling w/o a block because you want # to manage when the connection actually needs to happen.) def self.open(options = {}) p4 = create_p4(options) # Again, if we're calling using the block, we'll connect and disconnect. # Otherwise, just return the created p4 object. if block_given? begin p4.connect yield p4 rescue P4Exception => ex puts "trace:" ex.backtrace.each { |l| puts "\t#{l}" } raise make_p4_error(p4) end else return p4 end ensure p4.disconnect if block_given? && p4 && p4.connected? end PROPERTIES = [ :password, :port, :user, :api_level, :charset, :client, :host, :handler, :maxlocktime, :maxresults, :maxscanrows, :prog, :ticketfile ] def self.create_p4(options) p4 = P4.new init_p4(p4) PROPERTIES.each do|key| p4.method(key.to_s + '=').call(options[key]) if options[key] end # From the WTF department, if you think you don't want a charset set, # make damn sure P4Ruby isn't going to get confused. p4.charset = nil if p4.charset == 'none' # Make P4Ruby only raise exceptions if there are errors. Warnings # (such as 'no such file(s)' don't get the same treatment. p4.exception_level = P4::RAISE_ERRORS p4 end # Before we do anything, clear out any environment variables that may have # leaked in from your environment # # Some of our HTTP APIs (e.g, Git Fusion) *do* utilize environment setups # for a system user, that may be installed alongside this API. We want this # system to always explicitly state which configuration to use. (So, it's # not just a concern of our development environments.) def self.init_p4(p4) p4.client = 'invalid' p4.port = '' p4.host = '' p4.password = '' # Disable the use of ticket files. # It might happen that an admin may decide to use the p4 command line # client to log in using the same system user we're running web services # as. If we happen to point to that file (say, via defaults) this will # use the valid ticket in the file instead of what has been specified # via headers. if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ p4.ticket_file = 'nul' p4.enviro_file = 'nul' else p4.ticket_file = '/dev/null' p4.enviro_file = '/dev/null' end if ENV['P4TRUST'].nil? ENV['P4TRUST'] = HWSSettings.system.P4TRUST end end def self.make_p4_error(p4) if p4.messages && p4.messages.first m = p4.messages.first P4Error.new(m.msgid, m.severity, m.to_s) else P4Error.default_error($ERROR_INFO.to_s) end end def self.create_temp_client(p4) name = (0...8).map { (65 + rand(26)).chr }.join p4_root = init_temp_workspace_dir(name) init_temp_client(name, p4, p4_root) p4_root end # Generates a stream client based on an existing stream def self.create_temp_stream_client_duplicate(p4, src_client) name = (0...8).map { (65 + rand(26)).chr }.join p4_root = init_temp_workspace_dir(name) p4.client = name spec = p4.fetch_client spec._root = p4_root spec._client = name spec._stream = src_client._stream spec._view = nil p4.save_client(spec) p4_root end private def self.init_temp_workspace_dir(name) dir = File.join(HWSSettings.system.WORKSPACE_DIR, name) unless Dir.exist?(dir) FileUtils.mkpath(dir) FileUtils.chmod(0700, dir) end dir end # Creates a temporary "wide open" client def self.init_temp_client(name, p4, p4_root) p4.client = name spec = p4.fetch_client spec._root = p4_root spec._client = name # When there's one depot, the view generated maps //depot/... //view/... # instead of //depot/... //view/depot/... like it will with multiple depots. if (spec._view.length == 1) spec._view = ["//depot/... //#{name}/depot/..."] end # This will explode if another client is using this name (should be rare). # *Supposedly* it will automatically clean up too. p4.save_client(spec, '-x') end end
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 16196 | Doug Scheirer | Merge from main | ||
#5 | 16014 | Doug Scheirer | Merge down from main | ||
#4 | 15884 | Doug Scheirer | Merge from main | ||
#3 | 15845 | Doug Scheirer | Integ from main | ||
#2 | 15715 | Doug Scheirer | merge changes from main | ||
#1 | 15688 | Doug Scheirer |
Populate -o //guest/perforce_software/helix-web-services/... //guest/doug_scheirer/helix-web-services/.... |
||
//guest/perforce_software/helix-web-services/main/source/helix_web_services/lib/p4_util.rb | |||||
#1 | 15622 | tjuricek |
Move source code to 'source/' subdirectory of branch. build/ will remain where it is. |
||
//guest/perforce_software/helix-web-services/main/helix_web_services/lib/p4_util.rb | |||||
#8 | 15240 | tjuricek |
Set api level via request path on all Helix Versioning Engine methods. This will allow migration of applications to different P4D versions. Our internal methods (like project API) should attempt to handle backward compatibility similarly. P4WEBAPI-118 |
||
#7 | 15213 | tjuricek |
Disable the use of ticket files by P4API usage of HWS. If someone were to use, say, the p4 command line client to do some debugging on the same system account as HWS, you might get a ticket file in the default location. In which case the ticket that's used is the one stored, and not the one specified via the header (if the header happens to be incorrect). Which basically opens up access on that account via HWS. |
||
#6 | 15147 | tjuricek |
Enable submissions by files on stream clients This obeys our earlier rules: notably the local client must not have any local files open for edit. |
||
#5 | 15132 | tjuricek | Provde a basic submit -e mechanism on classic perforce workspaces. | ||
#4 | 15114 | tjuricek | While I'm seeing ticket's files generated for the 'super' user, it's possible that this login in the test suite has 'enabled it's usage'. | ||
#3 | 15113 | tjuricek | Set ticket_file to /dev/null to ensure the p4api doesn't try to use a local tickets file for auth | ||
#2 | 15032 | tjuricek |
Starting config and doc revisions. System is now broken while revisions underway. Configuration of the p4d connection is now done via a single HWSSettings middleware object injected into the Rack env. The HWSP4Cleanup middleware now cleans up any p4 injected into the Rack env. The Auth::App class now mostly just contains one method to generate a p4 ticket. /auth/v1/login. Added yard documentation for the main project. Yard docs have been reconfigured to dump into build/ directories. This should probably be done with each release. Hm... The top level rake file contains a task, 'all:doc', to update our documentation. This should probably be run for each checkin. Hm... Specs are now using Rack::Test on top of a 'live' p4d. I'd suggest you still use the p4util mechanism, which now dumps to a /tmp folder, so we can safely add P4IGNORE rules back into your local .p4config file. Old 'perforce' application now called 'helix_versioning_engine'. Removing cache data. Helix Sync may be slow. It may also get axed. We'll see. |
||
#1 | 13799 | tjuricek |
Start with branch specs hosting in a new monolithic 'helix web services' project. Converting from a microservice to a monolithic architecture due to resource constraints at getting a deployable system running. Additionally, since it's not expected that people will upgrade often, the major benefit of microservices - being able to add services individually without affecting others - is not really a major benefit. The Ruby SDK will be consolidated into a single 'helix web services client' project. It may end up being distributed via Rubygems. This only runs branch specs at the moment. I want to get a CD pipeline setup for the monolithic server before revising more methods. |