|
require 'net/http' |
|
require 'json' |
|
require 'time' |
|
require 'chronic' |
|
|
|
class TFSBuild |
|
|
|
ODATA_URL = URI.parse('http://127.0.0.1:8080') |
|
READ_TIMEOUT = 120 #seconds |
|
DEBUG = 0 |
|
MAPPING_FILE = 'assets/config/tfs_project_mapping.json' |
|
CACHE_FILE = 'assets/config/.tfs_build_request_cache.json' |
|
|
|
@@project_mapping = JSON.parse(IO.read(MAPPING_FILE)) |
|
|
|
#used to solve odata DDoS'ing problem until new rufus-sheduler |
|
@@cache = {} |
|
|
|
def debug |
|
DEBUG |
|
end |
|
|
|
def project_mapping |
|
@@project_mapping |
|
end |
|
|
|
# function to validate json |
|
def valid_json? (json) |
|
JSON.parse(json) |
|
return true |
|
rescue JSON::ParserError |
|
return false |
|
end |
|
|
|
def parse_milliseconds(datestring) |
|
datestring.scan(/[0-9]+/)[0].to_i |
|
end |
|
|
|
def parse_date(datestring) |
|
return Time.at(parse_milliseconds(datestring)/1000) |
|
end |
|
|
|
def get_tfs_project_build_percentage(build_infos) |
|
build_info = build_infos[0] |
|
prev_build_info = build_infos[1] |
|
|
|
return 0 if build_info["BuildFinished"] |
|
last_duration = ((prev_build_info["FinishTime"].scan(/[0-9]+/)[0].to_i - prev_build_info["StartTime"].scan(/[0-9]+/)[0].to_i) / 1000).round(0) |
|
current_duration = (Time.now.to_i - (build_info["StartTime"].scan(/[0-9]+/)[0].to_i / 1000)).round(0) |
|
return 99 if current_duration >= last_duration |
|
result = ((current_duration * 100) / last_duration).round(0) |
|
return 0 if result < 0 |
|
result |
|
end |
|
|
|
def get_start_date_for_filter |
|
Chronic.parse('6 months ago').strftime('%Y-%m-%dT00:00:00') |
|
end |
|
|
|
def get_active_request_count |
|
return 0 if not @@cache |
|
@@cache.count |
|
end |
|
def insert_to_cache(id) |
|
@@cache[id] = 1 |
|
IO.write(CACHE_FILE, @@cache.to_json) |
|
end |
|
def is_in_cache?(id) |
|
@@cache.has_key?(id) |
|
end |
|
def remove_from_cache(id) |
|
@@cache.delete(id) if @@cache.has_key?(id) |
|
IO.write(CACHE_FILE, @@cache.to_json) |
|
end |
|
def get_json_for_tfs_project(id, project_name, build_definition, top = 1, record_to_return = -1) |
|
return if is_in_cache?(id) or @@cache.count > @@project_mapping.count / 2 |
|
insert_to_cache(id) |
|
url = URI.encode("/Projects('#{project_name}')/Builds?$format=json&$orderby=StartTime desc&$top=#{top}&$select=Definition,RequestedFor,LastChangedOn,Status,StartTime,FinishTime&$filter=Definition eq '#{build_definition}' and Status ne 'Stopped' and StartTime gt datetime'#{get_start_date_for_filter}'") |
|
puts url if DEBUG > 2 |
|
http = Net::HTTP.new(ODATA_URL.host, ODATA_URL.port) |
|
http.read_timeout = READ_TIMEOUT |
|
request = Net::HTTP::Get.new(url) |
|
response = http.request(request) |
|
if not valid_json?(response.body) |
|
remove_from_cache(id) |
|
return |
|
end |
|
json = JSON.parse(response.body) |
|
puts json if DEBUG > 2 |
|
remove_from_cache(id) |
|
return json['d']['results'][record_to_return] if json and json['d'] and json['d']['results'] and record_to_return > -1 |
|
return json['d']['results'] if json and json['d'] and json['d']['results'] |
|
rescue Timeout::Error |
|
puts DateTime.now.to_s+' Timeout occured while requesting '+url |
|
remove_from_cache(id) |
|
rescue Errno::ECONNRESET => e |
|
puts DateTime.now.to_s+' Connection reset by peer exception occured while requesting '+url |
|
remove_from_cache(id) |
|
end |
|
end |
|
@TFSBuild = TFSBuild.new() |
|
@LastActiveRequests = 0 |
|
|
|
@TFSBuild.project_mapping.each do |title, project| |
|
current_status = nil |
|
|
|
scheduler_config = project['scheduler'] if project['scheduler'] |
|
scheduler_config = ['every' => '120s', 'timeout' => '300s'] if not project['scheduler']['every'] or not project['scheduler']['timeout'] |
|
every = scheduler_config['every'] |
|
timeout = scheduler_config['timeout'] |
|
SCHEDULER.every every, first_in: 0, timeout: timeout do |prj| |
|
last_status = current_status |
|
reqStarted = DateTime.now |
|
build_infos = @TFSBuild.get_json_for_tfs_project(title, project['project'], project['build_definition'], 2) |
|
next if not build_infos or not build_infos.kind_of?(Array) |
|
reqEnded = DateTime.now |
|
puts build_infos if @TFSBuild.debug > 2 |
|
build_info = build_infos[0] |
|
next if not build_info |
|
|
|
puts reqStarted.strftime('%F %T') + '-'+reqEnded.strftime('%F %T')+' ('+(reqEnded.to_time.to_i-reqStarted.to_time.to_i).to_s+'s):' if @TFSBuild.debug > 0 |
|
puts ' '+build_info['Definition']+' '+build_info["RequestedFor"]+' '+build_info['LastChangedOn']+' '[email protected]_date(build_info["LastChangedOn"]).strftime('%F %T') if @TFSBuild.debug > 1 |
|
puts ' '+build_infos[1]['Definition']+' '+build_infos[1]["RequestedFor"]+' '+build_infos[1]['LastChangedOn']+' '[email protected]_date(build_infos[1]["LastChangedOn"]).strftime('%F %T') if @TFSBuild.debug > 1 and build_infos[1] |
|
|
|
next if not build_info['Status'] |
|
current_status = build_info['Status'] |
|
if current_status == 'InProgress' |
|
percent = @TFSBuild.get_tfs_project_build_percentage(build_infos) |
|
end |
|
send_event(title, { |
|
currentResult: current_status, |
|
lastResult: last_status, |
|
timestamp: @TFSBuild.parse_date(build_info["LastChangedOn"]).strftime('%F %T'), |
|
timestamp2: @TFSBuild.parse_milliseconds(build_info["LastChangedOn"]), |
|
value: percent, |
|
description: build_info["Definition"], |
|
buildStarted: @TFSBuild.parse_date(build_info["StartTime"]).strftime('%F %T'), |
|
buildFinished: @TFSBuild.parse_date(build_info["FinishTime"]).strftime('%F %T'), |
|
requestedFor: build_info["RequestedFor"] |
|
}) |
|
currentActiveRequests = @TFSBuild.get_active_request_count |
|
send_event('TFSBuildActiveRequestCount', { current: currentActiveRequests, last: @LastActiveRequests}) |
|
@LastActiveRequests = currentActiveRequests |
|
end |
|
end |