vcseif 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/vcseif.rb +23 -0
- data/lib/vcseif/connection.rb +285 -0
- data/lib/vcseif/rally_vcs_connection.rb +925 -0
- data/lib/vcseif/utils/auxloader.rb +255 -0
- data/lib/vcseif/utils/exceptions.rb +101 -0
- data/lib/vcseif/utils/fuzzer.rb +71 -0
- data/lib/vcseif/utils/konfigger.rb +421 -0
- data/lib/vcseif/utils/lock_file.rb +90 -0
- data/lib/vcseif/utils/proctbl.rb +146 -0
- data/lib/vcseif/utils/rally_logger.rb +223 -0
- data/lib/vcseif/utils/time_file.rb +80 -0
- data/lib/vcseif/vcs_connector.rb +487 -0
- data/lib/vcseif/vcs_connector_driver.rb +227 -0
- data/lib/vcseif/vcs_connector_runner.rb +283 -0
- data/lib/version.rb +18 -0
- metadata +173 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
# Copyright 2001-2013 Rally Software Development Corp. All Rights Reserved.
|
2
|
+
require "yaml"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
require_relative "vcs_connector_runner"
|
6
|
+
require_relative "utils/rally_logger"
|
7
|
+
require_relative "utils/exceptions"
|
8
|
+
|
9
|
+
class VCSConnectorDriver
|
10
|
+
|
11
|
+
NUMBER_OF_REQUIRED_VCS_CONNECTOR_SECTIONS = 3
|
12
|
+
VCS_APP_ROOT = 'VCS_APP_ROOT'
|
13
|
+
|
14
|
+
attr_reader :script_name, :uber_log_name
|
15
|
+
attr_accessor :log
|
16
|
+
|
17
|
+
def initialize(script_name)
|
18
|
+
rationalizeEnvironment()
|
19
|
+
@script_name = script_name
|
20
|
+
logs_directory = "%s/logs" % ENV[VCS_APP_ROOT]
|
21
|
+
if not Dir.exists?(logs_directory)
|
22
|
+
begin
|
23
|
+
FileUtils.mkdir(logs_directory)
|
24
|
+
rescue
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@uber_log_name = "%s/%s.log" % [logs_directory, script_name]
|
28
|
+
end
|
29
|
+
|
30
|
+
def rationalizeEnvironment()
|
31
|
+
# set vcs_app_root to the directory where either <vcs>2rally.exe or <vcs>2rally.rb is located
|
32
|
+
if ENV.keys.include?('OCRA_EXECUTABLE')
|
33
|
+
vcs_app_root = File.dirname(ENV['OCRA_EXECUTABLE'].gsub(/\\/, '/'))
|
34
|
+
else
|
35
|
+
vcs_app_root = File.expand_path(File.dirname($PROGRAM_NAME))
|
36
|
+
end
|
37
|
+
|
38
|
+
unless ENV.keys.include?(VCS_APP_ROOT)
|
39
|
+
ENV[VCS_APP_ROOT] = vcs_app_root # make this available everywhere...
|
40
|
+
$LOAD_PATH.unshift(vcs_app_root)
|
41
|
+
$LOAD_PATH.unshift("#{vcs_app_root}/lib")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute(args)
|
46
|
+
@log = RallyLogger.new(@uber_log_name)
|
47
|
+
@log.info("#{@script_name} started with parameters of: #{args.join(', ')}")
|
48
|
+
|
49
|
+
ret_code = 0
|
50
|
+
for config_name in args do
|
51
|
+
config = getConnectorConfiguration(config_name)
|
52
|
+
next if config.nil?
|
53
|
+
ret_code = delegateToConnectorRunner(config)
|
54
|
+
@log.info("finished processing config #{config_name} with status code of #{ret_code}")
|
55
|
+
end
|
56
|
+
@log.info("#{@script_name} COMPLETED")
|
57
|
+
return 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def getConnectorConfiguration(config_name)
|
61
|
+
"""
|
62
|
+
Allow a config_name to map to a file with the same value
|
63
|
+
exsiting within the ENV[VCS_APP_ROOT]/configs directory.
|
64
|
+
If such a file exists, it must be in YAML format and have at least 3 sections.
|
65
|
+
Allow specifying a config_name by 'name', 'name.yml', 'name.yaml' or 'name.txt'.
|
66
|
+
"""
|
67
|
+
# suffix-full options: in ENV[VCS_APP_ROOT]/configs subdir
|
68
|
+
# suffix-less options: try '%s.yml' % config_name in ENV[VCS_APP_ROOT]/configs subdir
|
69
|
+
# try '%s.yaml' % config_name in ENV[VCS_APP_ROOT]/configs subdir
|
70
|
+
# try '%s.txt' % config_name in ENV[VCS_APP_ROOT]/configs subdir
|
71
|
+
# TODO: consider cleaning this processing up in another method devoted solely to the sussing the options above
|
72
|
+
|
73
|
+
config_dir = "%s/configs" % ENV[VCS_APP_ROOT]
|
74
|
+
ext_name = File.extname(config_name)
|
75
|
+
if ext_name != "" # suffix-full
|
76
|
+
config_file_path = '%s/%s' % [config_dir, config_name]
|
77
|
+
if Dir.glob("#{config_dir}/*").include?(config_name)
|
78
|
+
config_name = config_name.sub(/#{ext_name}$/, '')
|
79
|
+
end
|
80
|
+
else # suffix-less
|
81
|
+
conf_root = "%s/%s" % [config_dir, config_name]
|
82
|
+
config_file_path = conf_root
|
83
|
+
if File.exists?('%s.yml' % conf_root)
|
84
|
+
config_file_path = "%s.yml" % conf_root
|
85
|
+
elsif File.exists?('%s.yaml' % conf_root)
|
86
|
+
config_file_path = "%s.yaml" % conf_root
|
87
|
+
elsif File.exists?('%s.txt' % conf_root)
|
88
|
+
config_file_path = "%s.txt" % conf_root
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if not File.exists?(config_file_path)
|
93
|
+
problem = "Config file for name: %s not found" % config_name
|
94
|
+
@log.error(problem)
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
|
98
|
+
if not File.file?(config_file_path)
|
99
|
+
problem = 'Config file: %s is not a file' % config_file_path
|
100
|
+
@log.error(problem)
|
101
|
+
return nil
|
102
|
+
end
|
103
|
+
|
104
|
+
if not File.readable?(config_file_path)
|
105
|
+
problem = 'Config file: %s not a readable file' % config_file_path
|
106
|
+
@log.error(problem)
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
config = loadConfiguration(config_file_path)
|
111
|
+
config_file_name = config_file_path.split("/")[-1]
|
112
|
+
if not config.nil?
|
113
|
+
config['ConfigName'] = File.basename(config_file_name, '.*')
|
114
|
+
config['ConfigPath'] = config_file_path
|
115
|
+
end
|
116
|
+
|
117
|
+
return config
|
118
|
+
end
|
119
|
+
|
120
|
+
def loadConfiguration(config_file_path)
|
121
|
+
begin
|
122
|
+
content = "" and File.open(config_file_path, 'r') { |f| content = f.read() }
|
123
|
+
# As YAML forbids tab chars, we have to rid the content of tab chars,
|
124
|
+
# but emit a log warning that tabs were detected and transformed if we do
|
125
|
+
if content =~ /\t/
|
126
|
+
content.gsub!(/\t/, ' ')
|
127
|
+
@log.warning('One or more tab characters (\\t) was detected in the config file and transformed to spaces. YAML does not allow tab characters.')
|
128
|
+
end
|
129
|
+
config = YAML.load(content)
|
130
|
+
raise "config file: #{} is not a valid YAML file" unless config.class.name == 'Hash'
|
131
|
+
sections = config.keys
|
132
|
+
rescue Exception => ex
|
133
|
+
@log.error("Unable to load YAML from config_file '%s' : %s" % [config_file_path, ex.message])
|
134
|
+
return nil
|
135
|
+
end
|
136
|
+
|
137
|
+
if config.keys.length < NUMBER_OF_REQUIRED_VCS_CONNECTOR_SECTIONS
|
138
|
+
@log.error("config '%s' not in required YAML format or has too few sections" % config_file_path)
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
|
142
|
+
return config
|
143
|
+
end
|
144
|
+
|
145
|
+
def delegateToConnectorRunner(config)
|
146
|
+
"""
|
147
|
+
Given a Hash with the configuration content,
|
148
|
+
obtain the correct _X_ConnectorRunner instance and feed it
|
149
|
+
the config and call its run method.
|
150
|
+
"""
|
151
|
+
hits = config.keys.select {|key| key =~ /^\w+Connector$/}
|
152
|
+
if hits.nil? or hits.empty?
|
153
|
+
@log.error("Config %s lacks an entry identifying the EIF Connector type" % config['ConfigName'])
|
154
|
+
return 1
|
155
|
+
end
|
156
|
+
connector_type = hits.first
|
157
|
+
|
158
|
+
runner_name = '%sRunner' % connector_type
|
159
|
+
begin
|
160
|
+
connector_runner_class = Kernel.const_get(runner_name)
|
161
|
+
rescue => ex
|
162
|
+
problem = 'Unable to locate/load the %sRunner class. Is your vcseif gem properly installed?'
|
163
|
+
@log.fatal(problem % connector_type)
|
164
|
+
return 2
|
165
|
+
end
|
166
|
+
|
167
|
+
begin
|
168
|
+
connector_runner = connector_runner_class.new(config)
|
169
|
+
rescue => ex
|
170
|
+
@log.error("#{ex.message}")
|
171
|
+
problem = 'Unable to obtain an instance of the %sRunner class. Is your vcseif gem properly installed?'
|
172
|
+
@log.fatal(problem % connector_type)
|
173
|
+
return 3
|
174
|
+
end
|
175
|
+
|
176
|
+
ret_code = 0
|
177
|
+
blurtage = ""
|
178
|
+
begin
|
179
|
+
connector_runner.run()
|
180
|
+
rescue VCSEIF_Exceptions::ConfigurationError => ex
|
181
|
+
blurtage = ex.verbiage
|
182
|
+
err_text = "ERROR: #{@script_name} detected a FATAL configuration error, #{blurtage}."
|
183
|
+
log_entry = "FATAL: Configuration error: #{blurtage}"
|
184
|
+
recordAnomaly(ex, err_text, blurtage, log_entry)
|
185
|
+
ret_code = 4
|
186
|
+
rescue VCSEIF_Exceptions::UnrecoverableException => ex
|
187
|
+
blurtage = ex.verbiage
|
188
|
+
err_text = "ERROR: #{@script_name}, #{blurtage}."
|
189
|
+
log_entry = "UnrecoverableException: #{blurtage}"
|
190
|
+
recordAnomaly(ex, err_text, blurtage, log_entry)
|
191
|
+
ret_code = 5
|
192
|
+
rescue VCSEIF_Exceptions::OperationalError => ex
|
193
|
+
blurtage = ex.verbiage
|
194
|
+
err_text = "ERROR: #{@script_name} detected an OperationalError condition"
|
195
|
+
log_entry = "OperationalError: #{blurtage}"
|
196
|
+
recordAnomaly(ex, err_text, blurtage, log_entry)
|
197
|
+
ret_code = 6
|
198
|
+
rescue Exception => ex
|
199
|
+
blurtage = ex.message
|
200
|
+
err_text = "ERROR: #{@script_name} detected an Exception condition"
|
201
|
+
recordAnomaly(ex, err_text, blurtage, "")
|
202
|
+
ret_code = 9
|
203
|
+
end
|
204
|
+
|
205
|
+
return ret_code
|
206
|
+
end
|
207
|
+
|
208
|
+
def recordAnomaly(ex, issue, blurtage, log_entry)
|
209
|
+
$stderr.write("%s\n" % issue)
|
210
|
+
#$stderr.write("ERROR: #{blurtage}\n") if !blurtage.empty?
|
211
|
+
#$stderr.write("#{ex.message}\n")
|
212
|
+
@log.error(log_entry) if not log_entry.empty?
|
213
|
+
|
214
|
+
@log.entry("ERROR: #{blurtage} #{ex.message}")
|
215
|
+
@log.entry(ex.backtrace.join("\n"))
|
216
|
+
@log.write("call chain follows:")
|
217
|
+
# blurt out a formatted stack trace in outermost to innermost call sequence
|
218
|
+
for bt_line in ex.backtrace.reverse do
|
219
|
+
short_bt = bt_line.sub(/#{Dir.pwd}\//, "... /")
|
220
|
+
short_bt = short_bt.sub(/^.*\/gems\//, "... gems/")
|
221
|
+
@log.write(" #{short_bt}")
|
222
|
+
$stderr.write(" #{short_bt}\n")
|
223
|
+
end
|
224
|
+
@log.write("!!!")
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
# Copyright 2001-2013 Rally Software Development Corp. All Rights Reserved.
|
2
|
+
|
3
|
+
############################################################################################################
|
4
|
+
#
|
5
|
+
# vcs_connector_runner - detect commits in a VCS Repository (non-Rally) and add
|
6
|
+
# any unrecorded changeset (and change) information to
|
7
|
+
# an SCMRepository in a Rally Workspace
|
8
|
+
#
|
9
|
+
############################################################################################################
|
10
|
+
|
11
|
+
require 'date'
|
12
|
+
require 'time'
|
13
|
+
|
14
|
+
############################################################################################################
|
15
|
+
|
16
|
+
class VCSConnectorRunner
|
17
|
+
|
18
|
+
attr_reader :log, :config
|
19
|
+
# the following declarations are necessary for testing, otherwise nothing accesses them
|
20
|
+
# except the instance
|
21
|
+
attr_reader :logfile_name, :lockfile_name, :preview, :connector, :extension, :time_file
|
22
|
+
|
23
|
+
EXISTENCE_PROCLAMATION = %{
|
24
|
+
************************************************************************************************************
|
25
|
+
|
26
|
+
Rally VCSConnector starting at: %s with pid: %s
|
27
|
+
curdir: %s
|
28
|
+
command: %s
|
29
|
+
|
30
|
+
************************************************************************************************************
|
31
|
+
}
|
32
|
+
|
33
|
+
STD_TS_FMT = '%Y-%m-%d %H:%M:%S Z'
|
34
|
+
|
35
|
+
DEFAULT_ORIGIN_TIME = (DateTime.now - 3).to_time
|
36
|
+
############################################################################################################
|
37
|
+
|
38
|
+
|
39
|
+
def initialize(config)
|
40
|
+
"""
|
41
|
+
An instance of this class is instantiated with a Hash containing the
|
42
|
+
specifics of a configuration to direct the execution. Some rudimentary
|
43
|
+
validation of the configuration is done, via use of an instance of a
|
44
|
+
Konfabulator, with any detected problem noted and a ConfigurationError is
|
45
|
+
raised in that eventuality.
|
46
|
+
Once those items are taken care of, the instance obtains an instance of a
|
47
|
+
an AuxiliaryClassLoader instance and an instance of a VCSConnector.
|
48
|
+
The AuxiliaryClassLoader is delegated the task of pulling in any Extension classes.
|
49
|
+
These instances are then provided to the VCSConnector instance.
|
50
|
+
This instance then runs the operate_service method that directs the VCSConnector
|
51
|
+
instance to obtain unrecorded changesets from the target VCS and reflect them in
|
52
|
+
the Rally server.
|
53
|
+
"""
|
54
|
+
@logfile_name = "%s/logs/%s.log" % [ENV['VCS_APP_ROOT'], config['ConfigName']]
|
55
|
+
@lockfile_name = '%s/%s.LOCK' % [ENV['VCS_APP_ROOT'], config['ConfigName']]
|
56
|
+
@log = RallyLogger.new(@logfile_name)
|
57
|
+
VCSEIF_Exceptions::logAllExceptions(true, @log)
|
58
|
+
@config = config
|
59
|
+
@preview = false
|
60
|
+
@connector = nil
|
61
|
+
@extension = {}
|
62
|
+
@time_file = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def proclaim_existence
|
66
|
+
proc = ProcTable.target_process($$) # $$ is the Ruby shorthand for Process.pid
|
67
|
+
start_time = Time.now.utc.strftime(STD_TS_FMT)
|
68
|
+
cmd_elements = proc.cmdline.split()
|
69
|
+
cmd_elements[0] = File.basename(cmd_elements.first)
|
70
|
+
proc.cmdline = cmd_elements.join(" ")
|
71
|
+
@log.write(EXISTENCE_PROCLAMATION % [start_time, proc.pid, Dir.pwd, proc.cmdline])
|
72
|
+
end
|
73
|
+
|
74
|
+
def acquireLock
|
75
|
+
"""
|
76
|
+
Check for conditions to proceed based on lock file absence/presence status.
|
77
|
+
Acquisition of the lock is a green-light to proceed.
|
78
|
+
Failure to acquire the lock prevents any further operation.
|
79
|
+
"""
|
80
|
+
if LockFile.exists?(@lockfile_name)
|
81
|
+
@log.warning("A %s file exists" % @lockfile_name)
|
82
|
+
locker = LockFile.current_lock_holder(@lockfile_name)
|
83
|
+
if not LockFile.locker_is_running?(@lockfile_name, locker)
|
84
|
+
message = "A prior connector process (%s) did not clean up "
|
85
|
+
message << "the lock file on termination, proceeding with this run"
|
86
|
+
@log.warn(message % locker)
|
87
|
+
else
|
88
|
+
@log.error("Another connector process [%s] is still running, unable to proceed" % locker)
|
89
|
+
problem = "Simultaneous processes for this connector are prohibited"
|
90
|
+
boomex = VCSEIF_Exceptions::UnrecoverableException.new(problem)
|
91
|
+
raise boomex, problem
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
LockFile.create_lock(@lockfile_name)
|
96
|
+
return true
|
97
|
+
end
|
98
|
+
|
99
|
+
def releaseLock
|
100
|
+
LockFile.destroy_lock(@lockfile_name)
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
public
|
105
|
+
def run
|
106
|
+
proclaim_existence()
|
107
|
+
own_lock = false
|
108
|
+
own_lock = acquireLock()
|
109
|
+
|
110
|
+
begin
|
111
|
+
operateService()
|
112
|
+
rescue Exception => ex
|
113
|
+
raise
|
114
|
+
ensure
|
115
|
+
begin
|
116
|
+
releaseLock() if own_lock
|
117
|
+
rescue Exception => ex
|
118
|
+
problem = "ERROR: unable to remove lock file '%s', %s" % [@lockfile_name, ex.message]
|
119
|
+
shux = VCSEIF_Exceptions::OperationalError.new(problem)
|
120
|
+
raise shux, problem
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
private
|
128
|
+
def operateService()
|
129
|
+
started = finished = elapsed = nil
|
130
|
+
connector = nil
|
131
|
+
@log.info("processing to commence using content from the %s.yml config file" % @config['ConfigPath'])
|
132
|
+
|
133
|
+
conf_name = @config['ConfigName']
|
134
|
+
conf_path = @config['ConfigPath']
|
135
|
+
last_conf_mod = File.mtime(conf_path).utc.strftime(STD_TS_FMT)
|
136
|
+
blurb_vars = [ conf_path, last_conf_mod, File.size(conf_path) ]
|
137
|
+
@log.info("%s last modified %s, size: %d chars" % blurb_vars)
|
138
|
+
@config = inflateConfiguration(@config) # transform @config from Hash to Konfabulator instance
|
139
|
+
|
140
|
+
this_run = Time.now.utc # be optimistic that reflectChangesetsInRally will succeed
|
141
|
+
now_zulu = this_run.strftime(STD_TS_FMT) # zulu <-- universal coordinated time <-- UTC
|
142
|
+
|
143
|
+
@time_file = TimeFile.new(buildTimeFileName(conf_name), @log)
|
144
|
+
if File.exists?(@time_file.filename)
|
145
|
+
#last_commit_zulu_seconds = (@time_file.read() - 3600) # the 3600 seconds is a DST compensator
|
146
|
+
last_commit_zulu_seconds = @time_file.read()
|
147
|
+
last_commit = Time.at(last_commit_zulu_seconds).utc # convert epoch seconds value to Zulu time
|
148
|
+
else
|
149
|
+
last_commit = DEFAULT_ORIGIN_TIME
|
150
|
+
end
|
151
|
+
last_commit_zulu = last_commit.strftime(STD_TS_FMT)
|
152
|
+
log_msg = "Time File value %s --- Now %s" % [last_commit_zulu, now_zulu]
|
153
|
+
@log.info(log_msg)
|
154
|
+
|
155
|
+
connector = VCSConnector.new(@config, @log)
|
156
|
+
@log.debug("got an VCSConnector instance, calling its run method...")
|
157
|
+
status, repo_changesets = connector.run(last_commit, @extension)
|
158
|
+
finished = Time.now.utc
|
159
|
+
elapsed = finished - this_run
|
160
|
+
##
|
161
|
+
## puts "after VCSConnector.run, status: |#{status}| repo_changesets created to follow:"
|
162
|
+
## target_repo = repo_changesets.keys.first
|
163
|
+
## for repo_chgset in repo_changesets[target_repo] do
|
164
|
+
## name = repo_chgset['Name']
|
165
|
+
## tstamp = repo_chgset['CommitTimestamp'].sub('T', ' ')
|
166
|
+
## puts "%36.36s %-19.19s Z" % [name, tstamp]
|
167
|
+
## end
|
168
|
+
##
|
169
|
+
logServiceStatistics(repo_changesets, elapsed.round)
|
170
|
+
|
171
|
+
if @preview
|
172
|
+
@log.info("Preview mode in effect, Time File not written/updated")
|
173
|
+
return
|
174
|
+
end
|
175
|
+
if status != true
|
176
|
+
# Not writing the time.file may cause repetitive detection of changesets,
|
177
|
+
# but that is better than missing out on changesets altogether
|
178
|
+
@log.info("There was an error in processing so Time File was not written")
|
179
|
+
problem = "OperationalErrors detected during this processing run"
|
180
|
+
operr = VCSEIF_Exceptions::OperationalError.new(problem)
|
181
|
+
raise operr, problem
|
182
|
+
end
|
183
|
+
|
184
|
+
repo_name = repo_changesets.keys().first
|
185
|
+
changesets = repo_changesets[repo_name]
|
186
|
+
|
187
|
+
# we've added changesets successfully, so update the Time File (x_time.file)
|
188
|
+
# the time_file instance converts epoch seconds to a readable representation
|
189
|
+
# but we also emit a readable representation of this_run in the log with the desired context
|
190
|
+
updated_last_commit = calc_last_commit_time(changesets, last_commit)
|
191
|
+
@time_file.write(updated_last_commit)
|
192
|
+
@log.info("time file written with value of %s" % updated_last_commit)
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
public
|
197
|
+
|
198
|
+
def calc_last_commit_time(changesets, default)
|
199
|
+
return default if changesets.nil? || changesets.empty?
|
200
|
+
last = changesets.first # default
|
201
|
+
last = changesets[-2] if changesets.length >= 2
|
202
|
+
#the last commit we get is a timestamp value from Rally which is already in Zulu with milliseconds
|
203
|
+
calctime = DateTime.parse(last["CommitTimestamp"]).to_time
|
204
|
+
@log.debug("Calculated timefile value of #{calctime}")
|
205
|
+
calctime
|
206
|
+
end
|
207
|
+
|
208
|
+
def buildTimeFileName(config_name)
|
209
|
+
tf_name = "%s/time.file" % ENV['VCS_APP_ROOT']
|
210
|
+
if not config_name.empty?
|
211
|
+
tf_name = "%s/%s_time.file" % [ENV['VCS_APP_ROOT'], config_name]
|
212
|
+
end
|
213
|
+
return tf_name
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
private
|
218
|
+
def logServiceStatistics(repo_changeset, elapsed)
|
219
|
+
"""
|
220
|
+
what we intend to append to the log...
|
221
|
+
vcs_conn_config.yml: 32 additional VCS changesets reflected in Rally
|
222
|
+
and a line with the elapsed time taken in human readable form
|
223
|
+
(elapsed is in seconds (a float value))
|
224
|
+
"""
|
225
|
+
repo_changeset.each_pair do |repo, changesets|
|
226
|
+
preview_reminder = "" # default
|
227
|
+
preview_reminder = "(Preview Mode)" if @preview == true
|
228
|
+
cs_length = changesets.nil? ? 0 : changesets.length
|
229
|
+
@log.info("%d changesets created for %s %s" % [cs_length, repo, preview_reminder])
|
230
|
+
end
|
231
|
+
hours, rem = elapsed.divmod(3600)
|
232
|
+
mins, secs = rem.divmod( 60)
|
233
|
+
duration = "%d:%02d:%02d (hms)" % [hours, mins, secs]
|
234
|
+
@log.info("service run took %s" % duration)
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
public
|
239
|
+
def inflateConfiguration(config)
|
240
|
+
"""
|
241
|
+
Given a Hash with config info, obtain and return a Konfabulator instance
|
242
|
+
that incorporates that config information into a form that provides an easy to
|
243
|
+
use interface to access the configuration specification elements.
|
244
|
+
"""
|
245
|
+
begin
|
246
|
+
konfig = Konfabulator.new(config, @log)
|
247
|
+
rescue VCSEIF_Exceptions::ConfigurationError => ex
|
248
|
+
# info for this will have already been logged or blurted
|
249
|
+
raise
|
250
|
+
rescue VCSEIF_Exceptions::NonFatalConfigurationError => ex
|
251
|
+
# info for this will have already been logged or blurted
|
252
|
+
raise
|
253
|
+
rescue Exception => ex
|
254
|
+
raise StandardError, ex.message
|
255
|
+
end
|
256
|
+
|
257
|
+
svc_conf = konfig.topLevel('Services')
|
258
|
+
@preview = false
|
259
|
+
@preview = true if svc_conf and svc_conf['Preview'] == true
|
260
|
+
@log.info('Preview mode in effect') if @preview
|
261
|
+
@log_level = 'Info'
|
262
|
+
ll = svc_conf['LogLevel'] || nil
|
263
|
+
ll = ll.downcase.sub(/^(.)/) {$1.capitalize} unless ll.nil? # title case the result
|
264
|
+
if ['Fatal', 'Error', 'Warn', 'Info', 'Debug'].include?(ll)
|
265
|
+
@log_level = ll
|
266
|
+
@log.set_level(@log_level)
|
267
|
+
@log.info("LogLevel set to '%s'" % @log_level)
|
268
|
+
#else
|
269
|
+
# bad LogLevel specified, do nothing, we've already defaulted to Info level
|
270
|
+
end
|
271
|
+
|
272
|
+
# TODO: enable this when a well-defined need for it has been established
|
273
|
+
#if svc_conf.contains('PostBatchExtension')
|
274
|
+
# pba_class_name = svc_conf.get('PostBatchExtension')
|
275
|
+
# pba_class = ExtensionLoader.getExtension(pba_class_name)
|
276
|
+
# @extension['PostBatch'] = pba_class.new()
|
277
|
+
#end
|
278
|
+
|
279
|
+
return konfig
|
280
|
+
end
|
281
|
+
|
282
|
+
end
|
283
|
+
|