class Fastlane::Helper::MangoHelper
Attributes
Public Class Methods
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 12 def initialize(params) @container_name = params[:container_name] @no_vnc_port = params[:no_vnc_port] @device_name = params[:device_name] @docker_image = params[:docker_image] @timeout = params[:container_timeout] @sdk_path = params[:sdk_path] @port_factor = params[:port_factor].to_i @core_amount = params[:core_amount].to_i @maximal_run_time = params[:maximal_run_time] @sleep_interval = 5 @container = nil @adb_path = adb_path @is_running_on_emulator = params[:is_running_on_emulator] @pre_action = params[:pre_action] @docker_registry_login = params[:docker_registry_login] @pull_latest_image = params[:pull_latest_image] @environment_variables = params[:environment_variables] @vnc_enabled = params[:vnc_enabled] @docker_commander = DockerCommander.new(container_name) @emulator_commander = EmulatorCommander.new(container_name) end
Public Instance Methods
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 101 def kvm_disabled? begin @docker_commander.exec(command: 'kvm-ok > kvm-ok.txt') rescue StandardError # kvm-ok will always throw regardless of the result. therefore we save the output in the file and ignore the error end @docker_commander.exec(command: 'cat kvm-ok.txt').include?('KVM acceleration can NOT be used') end
Setting up the container:
-
Checks if ports are already allocated and kill the ones that do
-
Checks if Container we want to create already exist. If it does, restart it and check that the ports are correct
-
Creates container if there wasn't one already created or the created one has incorrect ports
-
Finally, waits until container is up and running (Healthy) using timeout specified in params
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 40 def setup_container assign_unique_vnc_port if port_factor && is_running_on_emulator if container_available? UI.important('Container was already started. Stopping and removing..') @docker_commander.delete_container end handle_ports_allocation if is_running_on_emulator && vnc_enabled pull_from_registry if @pull_latest_image # Make sure that network bridge for the current container is not already used @docker_commander.disconnect_network_bridge create_container if is_running_on_emulator && kvm_disabled? raise 'Linux requires GPU acceleration for running emulators, but KVM virtualization is not supported by your CPU. Exiting..' end container_state = wait_for_healthy_container if is_running_on_emulator && container_state connection_state = @emulator_commander.check_connection container_state = connection_state && connection_state end unless container_state UI.important("Will retry to create a healthy docker container after #{sleep_interval} seconds") @docker_commander.delete_container sleep @sleep_interval create_container unless wait_for_healthy_container UI.important('Container is unhealthy. Exiting..') begin Actions.sh("docker logs #{container_name} --tail 200") Actions.sh("docker exec -i #{container_name} cat /var/log/supervisor/docker-android.stderr.log") Actions.sh("docker exec -i #{container_name} cat /var/log/supervisor/supervisord.log") rescue StandardError # do nothing end # We use code "2" as we need something than just standard error code 1, so we can differentiate the next step in CI exit 2 end if is_running_on_emulator && !@emulator_commander.check_connection UI.important('Cannot connect to emulator. Exiting..') exit 2 end end if is_running_on_emulator @emulator_commander.disable_animations @emulator_commander.increase_logcat_storage end execute_pre_action if @pre_action end
Private Instance Methods
Sets path to adb
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 113 def adb_path "#{@sdk_path}/platform-tools/adb" end
assigns vnc port
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 118 def assign_unique_vnc_port @no_vnc_port = 6080 + port_factor @host_ip_address = `hostname -i | head -n1 | awk '{print $1;}'`.delete!("\n") UI.success("Port: #{@no_vnc_port} was chosen for VNC") UI.success("Link to VNC: http://#{@host_ip_address}:#{@no_vnc_port}") end
Checks if container is already available
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 207 def container_available? return false unless container_name all_containers = Docker::Container.all(all: true) all_containers.each do |container| if container.info['Names'].first[1..-1] == container_name @container = container return true end end false end
Checks if container status is 'healthy'
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 222 def container_is_healthy? if @container.json['State']['Health'] @container.json['State']['Health']['Status'] == 'healthy' else @container.json['State']['Status'] == 'running' end end
Gets container instance of allocated port
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 198 def container_of_allocated_port(port) Docker::Container.all.each do |container| public_ports = container.info['Ports'].map { |public_port| public_port['PublicPort'] } return container if public_ports.include? port end nil end
Creates new container using params
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 126 def create_container UI.important("Creating container: #{container_name}") CpuLoadHandler.print_cpu_load begin container = create_container_call set_container_name(container) rescue StandardError UI.important("Something went wrong while creating: #{container_name}, will retry in #{@sleep_interval} seconds") CpuLoadHandler.print_cpu_load @docker_commander.delete_container sleep @sleep_interval container = create_container_call set_container_name(container) end @container = get_container_instance(container) if @container.nil? sleep 3 @container = get_container_instance(container) end end
Call to create a container. We don't use Docker API here, as it doesn't support –privileged.
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 156 def create_container_call # When CPU is under load we cannot create a healthy container CpuLoadHandler.wait_cpu_to_idle additional_env = '' environment_variables.each do |variable| additional_env += " -e #{variable}" end emulator_args = is_running_on_emulator ? "-p #{no_vnc_port}:6080 -e DEVICE='#{device_name}'" : '' emulator_args = "#{emulator_args}#{additional_env}" @docker_commander.start_container(emulator_args: emulator_args, docker_image: docker_image, core_amount: core_amount) end
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 170 def execute_pre_action @docker_commander.exec(command: @pre_action) end
Gets container instance by container ID
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 149 def get_container_instance(container) Docker::Container.all(all: true).each do |cont| return cont if cont.id == container end end
Checks that chosen ports are not already allocated. If they are, it will stop the allocated container
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 183 def handle_ports_allocation vnc_allocated_container = container_of_allocated_port(no_vnc_port) if vnc_allocated_container UI.important("Port: #{no_vnc_port} was already allocated. Stopping Container.") vnc_allocated_container.stop end if port_open?('0.0.0.0', @no_vnc_port) UI.important('Something went wrong. VNC port is still busy') sleep @sleep_interval @docker_commander.delete_container end end
Checks if port is already openZ
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 258 def port_open?(server, port) http = Net::HTTP.start(server, port, open_timeout: 5, read_timeout: 5) response = http.head('/') response.code == '200' rescue Timeout::Error, SocketError, Errno::ECONNREFUSED false end
Pull the docker images before creating a container
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 175 def pull_from_registry UI.important('Pulling the :latest image from the registry') docker_image_name = docker_image.gsub(':latest', '') Actions.sh(@docker_registry_login) if @docker_registry_login @docker_commander.pull_image(docker_image_name: docker_image_name) end
if we do not have container name, we cane use container ID that we got from create call
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 267 def set_container_name(container) unless container_name @container_name = @emulator_commander.container_name = @docker_commander.container_name = container end end
Waits until container is healthy using specified timeout
# File lib/fastlane/plugin/mango/helper/mango_helper.rb, line 231 def wait_for_healthy_container UI.important('Waiting for Container to be in the Healthy state.') number_of_tries = timeout / sleep_interval number_of_tries.times do sleep sleep_interval / 2 if container_is_healthy? UI.success('Your container is ready to work') return true end if @container.json['State']['Health'] UI.important("Container status: #{@container.json['State']['Health']['Status']}") else UI.important("Container status: #{@container.json['State']['Status']}") end sleep sleep_interval end UI.important("The Container failed to load after '#{timeout}' seconds timeout. Reason: '#{@container.json['State']['Status']}'") false rescue StandardError => e puts e false end