vcseif 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|