Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pull sharing #11

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ COMMANDS
disconnect - Disconnect device(s) from local adb server and remove device(s) from user devices in STF
help - Shows a list of commands or help for one command
keys - Show available keys for filtering
share - Share available devices in STF with other agents and attach it to local adb server
values - Show known values for the filtering key

ENVIRONMENT VARIABLES
Expand Down
10 changes: 10 additions & 0 deletions lib/di.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
require 'stf/system/demonizer'

require 'stf/interactor/start_debug_session_interactor'
require 'stf/interactor/start_debug_share_interactor'
require 'stf/interactor/start_one_debug_session_interactor'
require 'stf/interactor/stop_debug_session_interactor'
require 'stf/interactor/stop_all_debug_sessions_interactor'
require 'stf/interactor/remove_all_user_devices_interactor'
require 'stf/interactor/get_keys_interactor'
require 'stf/interactor/get_values_interactor'
require 'stf/share/decision'
require 'stf/validate/uri_validator'

class DI
Expand All @@ -33,10 +35,18 @@ def init (opts = {})
-> {Stf::Client.new(opts[:url], opts[:token])},
memoize: true)

c.register(:share_decision,
-> {Stf::Decision.new},
memoize: true)

c.register(:start_debug_session_interactor,
-> {Stf::StartDebugSessionInteractor.new},
memoize: true)

c.register(:start_debug_share_interactor,
-> {Stf::StartDebugShareInteractor.new(c[:share_decision])},
memoize: true)

c.register(:start_one_debug_session_interactor,
-> {Stf::StartOneDebugSessionInteractor.new},
memoize: true)
Expand Down
2 changes: 1 addition & 1 deletion lib/stf/interactor/start_debug_session_interactor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def execute(opts = {})
filter = opts[:filter]
max_n = opts[:n].to_i > 0 ? opts[:n].to_i : 1
start_timeout = opts[:starttime].to_i > 0 ? opts[:starttime].to_i : 120
session = opts[:worktime].to_i > 0 ? opts[:session].to_i : 10800
session = opts[:session].to_i > 0 ? opts[:session].to_i : 10800
min_n = opts[:min].to_s.empty? ? (max_n + 1) / 2 : [opts[:min].to_i, max_n].min
healthcheck = opts[:health]
force_filter = opts[:forcefilter]
Expand Down
247 changes: 247 additions & 0 deletions lib/stf/interactor/start_debug_share_interactor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
require 'ADB'

require 'stf/client'
require 'stf/log/log'
require 'stf/model/device_list'

module Stf
class StartDebugShareInteractor

def initialize(decision)
@decision = decision
end

include Log
include ADB

def execute(opts = {})
nodaemon_flag = opts[:nodaemon]
filter = opts[:filter]
start_timeout = opts[:starttime].to_i > 0 ? opts[:starttime].to_i : 120
session_duration = opts[:session].to_i > 0 ? opts[:session].to_i : 10800
healthcheck = opts[:health]

connected = []

DI[:demonizer].kill unless opts[:nokill]

begin
start_loop(filter: filter,
healthcheck: healthcheck,
delay: 5,
timeout: start_timeout)

rescue SignalException => e
logger.info "Caught signal \"#{e.message}\""
DI[:stop_all_debug_sessions_interactor].execute
return false
rescue Exception => e
logger.info "Exception \"#{e.message}\" during initial connect loop"
DI[:stop_all_debug_sessions_interactor].execute
return false
end

logger.info "Lower quantity achieved"

my_name = get_my_name
logger.info "I am #{my_name}"

next_step = -> {
begin
connect_loop(filter: filter,
healthcheck: healthcheck,
delay: 5,
session_duration: session_duration,
laziness_delay: 2,
connected_stack: connected)

DI[:stop_all_debug_sessions_interactor].execute(byFilter: filter, nokill: true)

rescue SignalException => e
logger.info "Caught signal \"#{e.message}\""
DI[:stop_all_debug_sessions_interactor].execute
return false

rescue Exception => e
logger.error "Exception \"#{e.message}\" during lifecycle loop"
logger.error "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
DI[:stop_all_debug_sessions_interactor].execute
return false
end
}

if nodaemon_flag
next_step.call
else
# will be daemon here
DI[:demonizer].run do
next_step.call
end
end

true
end

def start_loop(wanted: 2,
filter: nil,
healthcheck: nil,
delay: 2,
timeout: 120)

finish_time = Time.now + timeout

while true do
cleanup_disconnected_devices(filter: filter, healthcheck: healthcheck)

if Time.now > finish_time
raise "Connect loop timeout reached"
end

all_devices = DeviceList.new(DI[:stf].get_devices)
stf_devices = all_devices.select_ready_to_connect
stf_devices = stf_devices.by_filter(filter) if filter
stf_devices = stf_devices.select_healthy_for_connect(healthcheck) if healthcheck

connected = devices & all_devices.as_connect_url_list
to_connect = wanted - connected.size

return if to_connect <= 0

if stf_devices.empty?
logger.error 'There is no available devices with criteria ' + filter
else
random_device = stf_devices.asArray.sample
DI[:start_one_debug_session_interactor].execute(random_device)
next # no delay for next device
end

sleep delay
end
end

def connect_loop(filter:, healthcheck:, delay:, session_duration:, laziness_delay:, connected_stack:)

finish_time = Time.now + session_duration

while true do
cleanup_disconnected_devices(filter: filter, healthcheck: healthcheck)

raise "Session timeout reached" if Time.now > finish_time

all_devices = DeviceList.new(DI[:stf].get_devices)
stf_devices = all_devices.by_filter(filter) if filter
stf_devices = stf_devices.select_healthy_for_connect(healthcheck) if healthcheck

connected = devices & all_devices.as_connect_url_list
free = stf_devices.select_ready_to_connect
brothers = stf_devices
.select_using_by_someone_else
.asArray
.group_by {|d| d.owner.name}
.map {|k, v| [k, v.length]}
.to_h

logger.debug "brothers #{brothers}"

big_brother = brothers.size > 0 ? brothers.values.max : 0

action = DI[:share_decision].tell_me(mine: connected.size,
brother: big_brother,
free: free.size)

logger.debug "action #{action}"

case action
when :take
take_device(devices: free, connected_stack: connected_stack)
next

when :lazyTake
if rand(brothers.size + 1) == 0
take_device(devices: free, connected_stack: connected_stack)
else
sleep laziness_delay
end
next

when :return
return_device(connected_stack: connected_stack)
next

when :lazyReturn
if rand(brothers.size + 1) == 0
return_device(connected_stack: connected_stack)
else
sleep laziness_delay
end
next

when :nothing
if big_brother - connected.size > 1
sleep laziness_delay
next
end

end

sleep delay
end
end

def take_device(devices:, connected_stack:)
if devices.nil? || devices.empty?
logger.error 'There is no available devices'
else
random_device = devices.asArray.sample
url = DI[:start_one_debug_session_interactor].execute(random_device)
connected_stack << url unless url.nil?
end
end

def return_device(connected_stack:)
url = connected_stack.pop
if url.nil?
url = devices.sample
end
DI[:stop_debug_session_interactor].execute(url) unless url.nil?
end

def get_my_name
my_devices = DI[:stf].get_user_devices
return nil if my_devices.nil? || my_devices.empty?
my_devices.pop.owner.name
end

def cleanup_disconnected_devices(filter:, healthcheck:)
to_disconnect = []
stf_devices = DeviceList.new(DI[:stf].get_user_devices)

if filter
disconnect_because_filter = stf_devices.except_filter(filter).as_connect_url_list
unless disconnect_because_filter.empty?
logger.info 'will be disconnected by filter: ' + disconnect_because_filter.join(',')
to_disconnect += disconnect_because_filter
end
end

if healthcheck
disconnect_by_health = stf_devices.select_not_healthy(healthcheck).as_connect_url_list
unless disconnect_by_health.empty?
logger.info 'will be disconnected by health check: ' + disconnect_by_health.join(',')
to_disconnect += disconnect_by_health
end
end

dead_persons = stf_devices.as_connect_url_list - devices
unless dead_persons.empty?
logger.info 'will be disconnected because not present locally: ' + dead_persons.join(',')
to_disconnect += dead_persons
end

to_disconnect.reject {|url| url.to_s.empty?}.uniq.each do |url|
logger.info 'Cleanup the device ' + url.to_s
DI[:stop_debug_session_interactor].execute(url)
end
end
end
end
4 changes: 2 additions & 2 deletions lib/stf/interactor/start_one_debug_session_interactor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def execute(device)
shell('echo adbtest', {serial: "#{result.remoteConnectUrl}"}, 30)
raise ADBError, "Could not execute shell test" unless stdout_contains "adbtest"

return true
return result.remoteConnectUrl

rescue StandardError, SignalException => e
begin
Expand All @@ -52,7 +52,7 @@ def execute(device)
end

logger.error "Failed to connect to #{serial}: " + e&.message
return false
return nil
end
end

Expand Down
20 changes: 16 additions & 4 deletions lib/stf/model/device_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ def except_filter(filter)
end

def select_healthy(pattern)
pattern ? select { |d| d.healthy?(pattern) } : this
pattern ? select {|d| d.healthy?(pattern)} : this
end

# more pessimistic than healthy()
def select_healthy_for_connect(pattern)
pattern ? select { |d| d.healthy_for_connect?(pattern) } : this
pattern ? select {|d| d.healthy_for_connect?(pattern)} : this
end

def select_not_healthy(pattern)
pattern ? reject { |d| d.healthy?(pattern) } : []
pattern ? reject {|d| d.healthy?(pattern)} : []
end

def select_ready_to_connect
Expand All @@ -52,8 +52,20 @@ def select_ready_to_connect
}
end

def select_using_by_someone_else
select {|d|
d.present == true &&
d.status == 3 &&
d.ready == true &&
d.using == false &&
!d.owner.nil? &&
!d.owner.name.nil? &&
!d.owner.name.empty?
}
end

def as_connect_url_list
@devices.map {|d| d.remoteConnectUrl}.reject { |c| c.nil? || c.empty? }
@devices.map {|d| d.remoteConnectUrl}.reject {|c| c.nil? || c.empty?}
end

def select
Expand Down
Loading