fastlane-plugin-mango 1.1.2 → 1.3.15
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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/lib/fastlane/plugin/mango/actions/run_dockerized_task_action.rb +90 -67
- data/lib/fastlane/plugin/mango/helper/cpu_load_handler.rb +39 -0
- data/lib/fastlane/plugin/mango/helper/docker_commander.rb +21 -30
- data/lib/fastlane/plugin/mango/helper/emulator_commander.rb +12 -4
- data/lib/fastlane/plugin/mango/helper/mango_helper.rb +41 -67
- data/lib/fastlane/plugin/mango/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 789e89cc42d904d6f20d595d4e4e5e536688431cef29839e784e6fbe8dfd7e13
|
|
4
|
+
data.tar.gz: e4143d7a96c169910ec836972f4362b0244828f1899a7837b33911b62f31e7f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0243850ec81a9f5108d5c6ca0a46770238fced448407bdb0c6abacb734bc7804bedc58880275187908cf2a7993a52c635d84749d155d62265e8401788b53dcd4'
|
|
7
|
+
data.tar.gz: f0dc414874f535ac73ba139fe5476516bc340d5b66b78fd162b6760ca4f783e4911d18ee4d36bec31032431869d093e6e364756ad7c216e6685bd91ae7846c61
|
data/README.md
CHANGED
|
@@ -78,6 +78,7 @@ The `mango` action has plenty of options to configure it.
|
|
|
78
78
|
| `docker_image` | Name of the Docker image, that should be started and used to run your tasks. | butomo1989/docker-android-x86-5.1.1 | ❌ | `String` |
|
|
79
79
|
| `container_timeout` | Timeout (in seconds) to get a healthy docker container. Depending on your `docker_image` it may take some time until it's started up and ready to use. | 450 (this equals 7.5 minutes) | ❌ | `Integer` |
|
|
80
80
|
| `android_task` | A generic Android task you want to execute. | - | ❌ | `String` |
|
|
81
|
+
| `core_amount` | Cpu core amount while starting docker container with limited resource. | - | ✅ | `Integer` |
|
|
81
82
|
| `sdk_path` | The path to your Android sdk directory. | `ANDROID_HOME` environment variable | ✅ | `String` |
|
|
82
83
|
| `port_factor` | Base for calculating a unique port for noVNC. We recommend to use the `EXECUTOR_NUMBER` from your Jenkins environment. | - | ✅ | `String` |
|
|
83
84
|
| `workspace_dir` | Path to the workspace to execute commands. If you want to execute your `android_task` from a different directory you have to specify `workspace_dir`. | `/root/tests/` | ✅ | `String` |
|
|
@@ -4,7 +4,9 @@ module Fastlane
|
|
|
4
4
|
module Actions
|
|
5
5
|
class RunDockerizedTaskAction < Action
|
|
6
6
|
def self.run(params)
|
|
7
|
-
UI.important(
|
|
7
|
+
UI.important('The mango plugin is working!')
|
|
8
|
+
workspace_dir = params[:workspace_dir]
|
|
9
|
+
ENV['DOCKER_CONFIG'] = "#{ENV['WORKSPACE']}/.docker"
|
|
8
10
|
mango_helper = Fastlane::Helper::MangoHelper.new(params)
|
|
9
11
|
mango_helper.setup_container
|
|
10
12
|
|
|
@@ -12,32 +14,32 @@ module Fastlane
|
|
|
12
14
|
|
|
13
15
|
failure_buffer_timeout = 5
|
|
14
16
|
timeout_command = "timeout #{params[:maximal_run_time] - failure_buffer_timeout}m"
|
|
15
|
-
workspace_dir = params[:workspace_dir]
|
|
16
17
|
|
|
17
18
|
android_task = params[:android_task]
|
|
18
19
|
if android_task
|
|
19
|
-
UI.success(
|
|
20
|
+
UI.success('Starting Android Task.')
|
|
20
21
|
bundle_install = params[:bundle_install] ? '&& bundle install ' : ''
|
|
21
22
|
|
|
22
23
|
docker_commander.exec(command: "cd #{workspace_dir} #{bundle_install}&& #{timeout_command} #{android_task} || exit 1")
|
|
23
24
|
end
|
|
24
|
-
|
|
25
25
|
ensure
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
docker_commander
|
|
26
|
+
begin
|
|
27
|
+
post_actions = params[:post_actions]
|
|
28
|
+
docker_commander&.exec(command: "cd #{workspace_dir} && #{post_actions}") if post_actions && !mango_helper.kvm_disabled?
|
|
29
|
+
|
|
30
|
+
UI.important("Cleaning up #{params[:emulator_name]} container")
|
|
31
|
+
docker_commander.delete_container if mango_helper&.instance_variable_get('@container')
|
|
32
|
+
rescue StandardError => e
|
|
33
|
+
puts e
|
|
29
34
|
end
|
|
30
|
-
|
|
31
|
-
UI.important("Cleaning up #{params[:emulator_name]} container")
|
|
32
|
-
mango_helper.clean_container if mango_helper.instance_variable_get('@container')
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
def self.description
|
|
36
|
-
|
|
38
|
+
'Action that runs Android tasks on a specified Docker image'
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
def self.authors
|
|
40
|
-
[
|
|
42
|
+
['Serghei Moret', 'Daniel Hartwich']
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
def self.return_value
|
|
@@ -50,123 +52,144 @@ module Fastlane
|
|
|
50
52
|
|
|
51
53
|
def self.details
|
|
52
54
|
# Optional:
|
|
53
|
-
|
|
55
|
+
''
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
def self.available_options
|
|
57
59
|
[
|
|
58
60
|
FastlaneCore::ConfigItem.new(key: :container_name,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
env_name: 'CONTAINER_NAME',
|
|
62
|
+
description: 'Name of the docker container. Will be generated randomly if not defined',
|
|
63
|
+
optional: true,
|
|
64
|
+
type: String),
|
|
63
65
|
|
|
64
66
|
FastlaneCore::ConfigItem.new(key: :no_vnc_port,
|
|
65
|
-
env_name:
|
|
66
|
-
description:
|
|
67
|
+
env_name: 'NO_VNC_PORT',
|
|
68
|
+
description: 'Port to redirect noVNC. 6080 by default',
|
|
67
69
|
default_value: 6080,
|
|
68
70
|
optional: false,
|
|
69
71
|
type: Integer),
|
|
70
72
|
|
|
71
73
|
FastlaneCore::ConfigItem.new(key: :device_name,
|
|
72
|
-
env_name:
|
|
73
|
-
description:
|
|
74
|
+
env_name: 'DEVICE_NAME',
|
|
75
|
+
description: 'Name of the Android device. Nexus 5X by default',
|
|
74
76
|
default_value: 'Nexus 5X',
|
|
75
77
|
optional: false,
|
|
76
78
|
type: String),
|
|
77
79
|
|
|
78
80
|
FastlaneCore::ConfigItem.new(key: :emulator_name,
|
|
79
|
-
env_name:
|
|
80
|
-
description:
|
|
81
|
+
env_name: 'EMULATOR_NAME',
|
|
82
|
+
description: 'Name of the Android emulator. emulator-5554 by default',
|
|
81
83
|
default_value: 'emulator-5554',
|
|
82
84
|
optional: false,
|
|
83
85
|
type: String),
|
|
84
86
|
|
|
85
87
|
FastlaneCore::ConfigItem.new(key: :docker_image,
|
|
86
|
-
env_name:
|
|
87
|
-
description:
|
|
88
|
+
env_name: 'DOCKER_IMAGE',
|
|
89
|
+
description: 'Name of the Docker image. butomo1989/docker-android-x86-5.1.1 by default',
|
|
88
90
|
default_value: 'butomo1989/docker-android-x86-5.1.1',
|
|
89
91
|
optional: false,
|
|
90
92
|
type: String),
|
|
91
93
|
|
|
92
94
|
FastlaneCore::ConfigItem.new(key: :container_timeout,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
95
|
+
env_name: 'CONTAINER_TIMEOUT',
|
|
96
|
+
description: 'Timeout (in seconds) to get the healthy docker container. 450 (7.5 minutes) by default',
|
|
97
|
+
default_value: 450,
|
|
98
|
+
optional: false,
|
|
99
|
+
type: Integer),
|
|
100
|
+
|
|
101
|
+
FastlaneCore::ConfigItem.new(key: :android_task,
|
|
102
|
+
env_name: 'ANDROID TASK',
|
|
103
|
+
description: 'A generic Android task you want to execute',
|
|
104
|
+
is_string: true,
|
|
105
|
+
optional: false),
|
|
104
106
|
|
|
105
107
|
FastlaneCore::ConfigItem.new(key: :sdk_path,
|
|
106
|
-
env_name:
|
|
107
|
-
description:
|
|
108
|
+
env_name: 'SDK_PATH',
|
|
109
|
+
description: 'The path to your Android sdk directory (root). ANDROID_HOME by default',
|
|
108
110
|
default_value: ENV['ANDROID_HOME'],
|
|
109
111
|
is_string: true,
|
|
110
112
|
optional: true),
|
|
111
113
|
|
|
112
114
|
FastlaneCore::ConfigItem.new(key: :port_factor,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
env_name: 'PORT_FACTOR',
|
|
116
|
+
description: 'Base for calculating a unique port for noVNC. You can pass EXECUTOR_NUMBER from Jenkins for example, this will be unique and not clash in case of several instances running on the same machine',
|
|
117
|
+
optional: true,
|
|
118
|
+
type: String),
|
|
119
|
+
|
|
120
|
+
FastlaneCore::ConfigItem.new(key: :core_amount,
|
|
121
|
+
env_name: 'CORE_AMOUNT',
|
|
122
|
+
default_value: 0,
|
|
123
|
+
description: 'Define if we want to start docker container with the limitation',
|
|
124
|
+
optional: true,
|
|
125
|
+
type: Integer),
|
|
117
126
|
|
|
118
127
|
FastlaneCore::ConfigItem.new(key: :workspace_dir,
|
|
119
|
-
env_name:
|
|
128
|
+
env_name: 'WORKSPACE_DIR',
|
|
120
129
|
default_value: '/root/tests/',
|
|
121
|
-
description:
|
|
130
|
+
description: 'Path to the workspace to execute commands. If you want to execute your `android_task` from a different directory you have to specify `workspace_dir`',
|
|
122
131
|
optional: true,
|
|
123
132
|
type: String),
|
|
124
133
|
|
|
125
134
|
FastlaneCore::ConfigItem.new(key: :maximal_run_time,
|
|
126
|
-
env_name:
|
|
135
|
+
env_name: 'MAXIMAL_RUN_TIME',
|
|
127
136
|
default_value: 60,
|
|
128
|
-
description:
|
|
137
|
+
description: 'Defines the maximal time of your test run. Defaults to 60 minutes',
|
|
129
138
|
optional: true,
|
|
130
139
|
type: Integer),
|
|
131
140
|
|
|
132
141
|
FastlaneCore::ConfigItem.new(key: :bundle_install,
|
|
133
|
-
env_name:
|
|
142
|
+
env_name: 'BUNDLE_INSTALL',
|
|
134
143
|
default_value: false,
|
|
135
|
-
description:
|
|
144
|
+
description: 'Defines if the Android task must execute bundle install before running a build',
|
|
136
145
|
optional: true,
|
|
137
146
|
type: Boolean),
|
|
138
147
|
|
|
139
148
|
FastlaneCore::ConfigItem.new(key: :is_running_on_emulator,
|
|
140
|
-
env_name:
|
|
149
|
+
env_name: 'IS_RUNNING_ON_EMULATOR',
|
|
141
150
|
default_value: true,
|
|
142
|
-
description:
|
|
151
|
+
description: 'Define if we want to run the emulator in the container',
|
|
143
152
|
optional: true,
|
|
144
153
|
type: Boolean),
|
|
145
154
|
|
|
146
155
|
FastlaneCore::ConfigItem.new(key: :post_actions,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
env_name: 'POST_ACTIONS',
|
|
157
|
+
description: 'Actions that will be executed after the main command has been executed',
|
|
158
|
+
is_string: true,
|
|
159
|
+
optional: true),
|
|
151
160
|
|
|
152
161
|
FastlaneCore::ConfigItem.new(key: :pre_action,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
env_name: 'PRE_ACTION',
|
|
163
|
+
description: 'Actions that will be executed before the docker image gets pulled',
|
|
164
|
+
is_string: true,
|
|
165
|
+
optional: true),
|
|
157
166
|
|
|
158
167
|
FastlaneCore::ConfigItem.new(key: :docker_registry_login,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
env_name: 'DOCKER_REGISTRY_LOGIN',
|
|
169
|
+
description: 'Authenticating yourself to a custom Docker registry',
|
|
170
|
+
type: String,
|
|
171
|
+
optional: true),
|
|
163
172
|
|
|
164
173
|
FastlaneCore::ConfigItem.new(key: :pull_latest_image,
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
env_name: 'PULL_LATEST_IMAGE',
|
|
175
|
+
description: 'Define if you want to pull the latest image',
|
|
176
|
+
type: Boolean,
|
|
177
|
+
default_value: false,
|
|
178
|
+
optional: true),
|
|
179
|
+
|
|
180
|
+
FastlaneCore::ConfigItem.new(key: :environment_variables,
|
|
181
|
+
env_name: 'ENVIRONMENT_VARIABLES',
|
|
182
|
+
description: 'Comma seperated list of environment variables which are passed into the docker container',
|
|
183
|
+
type: Array,
|
|
184
|
+
default_value: [],
|
|
185
|
+
optional: true),
|
|
186
|
+
|
|
187
|
+
FastlaneCore::ConfigItem.new(key: :vnc_enabled,
|
|
188
|
+
env_name: 'VNC_ENABLED',
|
|
189
|
+
description: 'A bool. True for vnc_enabled False for vnc_disabled',
|
|
190
|
+
type: Boolean,
|
|
191
|
+
default_value: true,
|
|
192
|
+
optional: true)
|
|
170
193
|
]
|
|
171
194
|
end
|
|
172
195
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'os'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
module Helper
|
|
5
|
+
module CpuLoadHandler
|
|
6
|
+
def self.print_cpu_load(load = cpu_load)
|
|
7
|
+
UI.important("CPU load is: #{load}") if load
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Gets load average of Linux machine
|
|
11
|
+
def self.cpu_load
|
|
12
|
+
load = Actions.sh('cat /proc/loadavg')
|
|
13
|
+
load.split(' ').first.to_f unless load.empty?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Gets amount of the CPU cores
|
|
17
|
+
def self.cpu_core_amount
|
|
18
|
+
Actions.sh("cat /proc/cpuinfo | awk '/^processor/{print $3}' | tail -1")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# For half an hour waiting until CPU load average is less than number of cores*2 (which considers that CPU is ready)
|
|
22
|
+
# Raises when 30 minutes timeout exceeds
|
|
23
|
+
def self.wait_cpu_to_idle
|
|
24
|
+
if OS.linux?
|
|
25
|
+
30.times do
|
|
26
|
+
load = cpu_load
|
|
27
|
+
return true if load <= cpu_core_amount.to_i * 1.5
|
|
28
|
+
print_cpu_load(load)
|
|
29
|
+
UI.important('Waiting for available resources..')
|
|
30
|
+
sleep 60
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
return true
|
|
34
|
+
end
|
|
35
|
+
raise "CPU was overloaded. Couldn't start emulator"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
require 'docker'
|
|
2
|
-
require 'os'
|
|
3
2
|
|
|
4
3
|
module Fastlane
|
|
5
4
|
module Helper
|
|
6
5
|
class DockerCommander
|
|
7
|
-
|
|
8
6
|
attr_accessor :container_name
|
|
9
7
|
|
|
10
8
|
def initialize(container_name)
|
|
@@ -12,35 +10,43 @@ module Fastlane
|
|
|
12
10
|
end
|
|
13
11
|
|
|
14
12
|
def pull_image(docker_image_name:)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
Actions.sh("docker pull #{docker_image_name}")
|
|
14
|
+
rescue StandardError => exception
|
|
15
|
+
prune if exception.message =~ /Create more free space in thin pool/
|
|
16
|
+
Actions.sh("docker pull #{docker_image_name}")
|
|
18
17
|
end
|
|
19
18
|
|
|
20
|
-
def start_container(emulator_args:, docker_image:)
|
|
19
|
+
def start_container(emulator_args:, docker_image:, core_amount:)
|
|
20
|
+
retries ||= 0
|
|
21
21
|
docker_name = if container_name
|
|
22
22
|
"--name #{container_name}"
|
|
23
23
|
else
|
|
24
24
|
''
|
|
25
25
|
end
|
|
26
|
+
# if core_amount value is defined then limit the container while starting
|
|
27
|
+
core_amount = if core_amount && core_amount > 0
|
|
28
|
+
"--cpus=#{core_amount}"
|
|
29
|
+
else
|
|
30
|
+
''
|
|
31
|
+
end
|
|
26
32
|
|
|
27
33
|
# Action.sh returns all output that the command produced but we are only
|
|
28
34
|
# interested in the last line, since it contains the id of the created container.
|
|
29
35
|
UI.important("Attaching #{ENV['PWD']} to the docker container")
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
Actions.sh("docker run -v $PWD:/root/tests --privileged -t -d #{core_amount} #{emulator_args} #{docker_name} #{docker_image}").chomp
|
|
37
|
+
rescue StandardError => exception
|
|
38
|
+
if exception.message =~ /Create more free space in thin pool/ && (retries += 1) < 2
|
|
39
|
+
prune
|
|
40
|
+
retry
|
|
32
41
|
end
|
|
33
42
|
end
|
|
34
43
|
|
|
35
|
-
def stop_container
|
|
36
|
-
Actions.sh("docker stop #{container_name}") if container_name
|
|
37
|
-
end
|
|
38
|
-
|
|
39
44
|
def delete_container
|
|
40
|
-
Actions.sh("docker rm #{container_name}") if container_name
|
|
45
|
+
Actions.sh("docker rm -f #{container_name}") if container_name
|
|
41
46
|
end
|
|
42
47
|
|
|
43
48
|
def disconnect_network_bridge
|
|
49
|
+
UI.important('Disconnecting from the network bridge')
|
|
44
50
|
Actions.sh("docker network disconnect -f bridge #{container_name}") if container_name
|
|
45
51
|
rescue StandardError
|
|
46
52
|
# Do nothing if the network bridge is already gone
|
|
@@ -53,25 +59,10 @@ module Fastlane
|
|
|
53
59
|
raise('Cannot execute docker command because the container name is unknown')
|
|
54
60
|
end
|
|
55
61
|
end
|
|
56
|
-
|
|
62
|
+
|
|
57
63
|
def prune
|
|
58
64
|
Action.sh('docker system prune -f')
|
|
59
65
|
end
|
|
60
|
-
|
|
61
|
-
def handle_thin_pool_exception(&block)
|
|
62
|
-
begin
|
|
63
|
-
block.call
|
|
64
|
-
rescue FastlaneCore::Interface::FastlaneShellError => exception
|
|
65
|
-
retry_counter = retry_counter.to_i + 1
|
|
66
|
-
if exception.message =~ /Create more free space in thin pool/ && retry_counter < 2
|
|
67
|
-
prune
|
|
68
|
-
retry
|
|
69
|
-
else
|
|
70
|
-
raise exception
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
66
|
end
|
|
76
67
|
end
|
|
77
|
-
end
|
|
68
|
+
end
|
|
@@ -2,9 +2,7 @@ require_relative 'docker_commander'
|
|
|
2
2
|
|
|
3
3
|
module Fastlane
|
|
4
4
|
module Helper
|
|
5
|
-
|
|
6
5
|
class EmulatorCommander
|
|
7
|
-
|
|
8
6
|
attr_accessor :container_name
|
|
9
7
|
|
|
10
8
|
def initialize(container_name)
|
|
@@ -22,7 +20,7 @@ module Fastlane
|
|
|
22
20
|
# it recovers after some time, so wait and retry
|
|
23
21
|
retry_counter = retry_counter.to_i + 1
|
|
24
22
|
if retry_counter <= 5
|
|
25
|
-
sleep 10*retry_counter
|
|
23
|
+
sleep 10 * retry_counter
|
|
26
24
|
retry
|
|
27
25
|
else
|
|
28
26
|
raise e
|
|
@@ -50,7 +48,17 @@ module Fastlane
|
|
|
50
48
|
def emulator_is_healthy?
|
|
51
49
|
list_devices = @docker_commander.exec(command: 'adb devices')
|
|
52
50
|
list_devices.include? "\tdevice"
|
|
51
|
+
rescue FastlaneCore::Interface::FastlaneShellError => e
|
|
52
|
+
# Under weird circumstances it can happen that adb is running but adb is not completely up
|
|
53
|
+
# it recovers after some time, so wait and retry
|
|
54
|
+
retry_counter = retry_counter.to_i + 1
|
|
55
|
+
if retry_counter <= 5
|
|
56
|
+
sleep 10 * retry_counter
|
|
57
|
+
retry
|
|
58
|
+
else
|
|
59
|
+
raise e
|
|
60
|
+
end
|
|
53
61
|
end
|
|
54
62
|
end
|
|
55
63
|
end
|
|
56
|
-
end
|
|
64
|
+
end
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
require 'docker'
|
|
2
|
-
require 'timeout'
|
|
3
|
-
require 'os'
|
|
4
1
|
require 'net/http'
|
|
5
2
|
require_relative 'docker_commander'
|
|
6
3
|
require_relative 'emulator_commander'
|
|
4
|
+
require_relative 'cpu_load_handler'
|
|
7
5
|
|
|
8
6
|
module Fastlane
|
|
9
7
|
module Helper
|
|
10
8
|
class MangoHelper
|
|
11
|
-
attr_reader :container_name, :no_vnc_port, :device_name, :docker_image, :timeout, :port_factor, :maximal_run_time, :sleep_interval, :is_running_on_emulator
|
|
9
|
+
attr_reader :container_name, :no_vnc_port, :device_name, :docker_image, :timeout, :port_factor, :maximal_run_time, :sleep_interval, :is_running_on_emulator, :environment_variables, :vnc_enabled, :core_amount
|
|
12
10
|
|
|
13
11
|
def initialize(params)
|
|
14
12
|
@container_name = params[:container_name]
|
|
@@ -18,6 +16,7 @@ module Fastlane
|
|
|
18
16
|
@timeout = params[:container_timeout]
|
|
19
17
|
@sdk_path = params[:sdk_path]
|
|
20
18
|
@port_factor = params[:port_factor].to_i
|
|
19
|
+
@core_amount = params[:core_amount].to_i
|
|
21
20
|
@maximal_run_time = params[:maximal_run_time]
|
|
22
21
|
@sleep_interval = 5
|
|
23
22
|
@container = nil
|
|
@@ -26,7 +25,8 @@ module Fastlane
|
|
|
26
25
|
@pre_action = params[:pre_action]
|
|
27
26
|
@docker_registry_login = params[:docker_registry_login]
|
|
28
27
|
@pull_latest_image = params[:pull_latest_image]
|
|
29
|
-
|
|
28
|
+
@environment_variables = params[:environment_variables]
|
|
29
|
+
@vnc_enabled = params[:vnc_enabled]
|
|
30
30
|
@docker_commander = DockerCommander.new(container_name)
|
|
31
31
|
@emulator_commander = EmulatorCommander.new(container_name)
|
|
32
32
|
end
|
|
@@ -40,13 +40,11 @@ module Fastlane
|
|
|
40
40
|
assign_unique_vnc_port if port_factor && is_running_on_emulator
|
|
41
41
|
|
|
42
42
|
if container_available?
|
|
43
|
-
|
|
44
|
-
@
|
|
43
|
+
UI.important('Container was already started. Stopping and removing..')
|
|
44
|
+
@docker_commander.delete_container
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
handle_ports_allocation if is_running_on_emulator
|
|
48
|
-
|
|
49
|
-
execute_pre_action if @pre_action
|
|
47
|
+
handle_ports_allocation if is_running_on_emulator && vnc_enabled
|
|
50
48
|
|
|
51
49
|
pull_from_registry if @pull_latest_image
|
|
52
50
|
|
|
@@ -67,14 +65,20 @@ module Fastlane
|
|
|
67
65
|
end
|
|
68
66
|
|
|
69
67
|
unless container_state
|
|
70
|
-
UI.important("Will retry
|
|
71
|
-
@
|
|
72
|
-
@container.delete(force: true)
|
|
68
|
+
UI.important("Will retry to create a healthy docker container after #{sleep_interval} seconds")
|
|
69
|
+
@docker_commander.delete_container
|
|
73
70
|
sleep @sleep_interval
|
|
74
71
|
create_container
|
|
75
72
|
|
|
76
73
|
unless wait_for_healthy_container
|
|
77
74
|
UI.important('Container is unhealthy. Exiting..')
|
|
75
|
+
begin
|
|
76
|
+
Actions.sh("docker logs #{container_name} --tail 200")
|
|
77
|
+
Actions.sh("docker exec -i #{container_name} cat /var/log/supervisor/docker-android.stderr.log")
|
|
78
|
+
Actions.sh("docker exec -i #{container_name} cat /var/log/supervisor/supervisord.log")
|
|
79
|
+
rescue StandardError
|
|
80
|
+
# do nothing
|
|
81
|
+
end
|
|
78
82
|
# We use code "2" as we need something than just standard error code 1, so we can differentiate the next step in CI
|
|
79
83
|
exit 2
|
|
80
84
|
end
|
|
@@ -89,6 +93,8 @@ module Fastlane
|
|
|
89
93
|
@emulator_commander.disable_animations
|
|
90
94
|
@emulator_commander.increase_logcat_storage
|
|
91
95
|
end
|
|
96
|
+
|
|
97
|
+
execute_pre_action if @pre_action
|
|
92
98
|
end
|
|
93
99
|
|
|
94
100
|
def kvm_disabled?
|
|
@@ -100,12 +106,6 @@ module Fastlane
|
|
|
100
106
|
@docker_commander.exec(command: 'cat kvm-ok.txt').include?('KVM acceleration can NOT be used')
|
|
101
107
|
end
|
|
102
108
|
|
|
103
|
-
# Stops and remove container
|
|
104
|
-
def clean_container
|
|
105
|
-
@container.stop
|
|
106
|
-
@container.delete(force: true)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
109
|
private
|
|
110
110
|
|
|
111
111
|
# Sets path to adb
|
|
@@ -116,7 +116,7 @@ module Fastlane
|
|
|
116
116
|
# assigns vnc port
|
|
117
117
|
def assign_unique_vnc_port
|
|
118
118
|
@no_vnc_port = 6080 + port_factor
|
|
119
|
-
@host_ip_address = `hostname -
|
|
119
|
+
@host_ip_address = `hostname -i | head -n1 | awk '{print $1;}'`.delete!("\n")
|
|
120
120
|
UI.success("Port: #{@no_vnc_port} was chosen for VNC")
|
|
121
121
|
UI.success("Link to VNC: http://#{@host_ip_address}:#{@no_vnc_port}")
|
|
122
122
|
end
|
|
@@ -124,49 +124,54 @@ module Fastlane
|
|
|
124
124
|
# Creates new container using params
|
|
125
125
|
def create_container
|
|
126
126
|
UI.important("Creating container: #{container_name}")
|
|
127
|
-
print_cpu_load
|
|
127
|
+
CpuLoadHandler.print_cpu_load
|
|
128
128
|
begin
|
|
129
129
|
container = create_container_call
|
|
130
130
|
set_container_name(container)
|
|
131
131
|
rescue StandardError
|
|
132
132
|
UI.important("Something went wrong while creating: #{container_name}, will retry in #{@sleep_interval} seconds")
|
|
133
|
-
print_cpu_load
|
|
134
|
-
@docker_commander.stop_container
|
|
133
|
+
CpuLoadHandler.print_cpu_load
|
|
135
134
|
@docker_commander.delete_container
|
|
136
|
-
|
|
137
135
|
sleep @sleep_interval
|
|
138
136
|
container = create_container_call
|
|
139
137
|
set_container_name(container)
|
|
140
138
|
end
|
|
141
|
-
get_container_instance(container)
|
|
139
|
+
@container = get_container_instance(container)
|
|
140
|
+
|
|
141
|
+
if @container.nil?
|
|
142
|
+
sleep 3
|
|
143
|
+
@container = get_container_instance(container)
|
|
144
|
+
end
|
|
142
145
|
end
|
|
143
146
|
|
|
144
147
|
# Gets container instance by container ID
|
|
145
148
|
def get_container_instance(container)
|
|
146
149
|
Docker::Container.all(all: true).each do |cont|
|
|
147
|
-
if cont.id == container
|
|
148
|
-
@container = cont
|
|
149
|
-
break
|
|
150
|
-
end
|
|
150
|
+
return cont if cont.id == container
|
|
151
151
|
end
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
# Call to create a container. We don't use Docker API here, as it doesn't support --privileged.
|
|
155
155
|
def create_container_call
|
|
156
156
|
# When CPU is under load we cannot create a healthy container
|
|
157
|
-
wait_cpu_to_idle
|
|
157
|
+
CpuLoadHandler.wait_cpu_to_idle
|
|
158
158
|
|
|
159
|
+
additional_env = ''
|
|
160
|
+
environment_variables.each do |variable|
|
|
161
|
+
additional_env += " -e #{variable}"
|
|
162
|
+
end
|
|
159
163
|
emulator_args = is_running_on_emulator ? "-p #{no_vnc_port}:6080 -e DEVICE='#{device_name}'" : ''
|
|
160
|
-
|
|
161
|
-
@docker_commander.start_container(emulator_args: emulator_args, docker_image: docker_image)
|
|
164
|
+
emulator_args = "#{emulator_args}#{additional_env}"
|
|
165
|
+
@docker_commander.start_container(emulator_args: emulator_args, docker_image: docker_image, core_amount: core_amount)
|
|
162
166
|
end
|
|
163
167
|
|
|
164
168
|
def execute_pre_action
|
|
165
|
-
|
|
169
|
+
@docker_commander.exec(command: @pre_action)
|
|
166
170
|
end
|
|
167
171
|
|
|
168
172
|
# Pull the docker images before creating a container
|
|
169
173
|
def pull_from_registry
|
|
174
|
+
UI.important('Pulling the :latest image from the registry')
|
|
170
175
|
docker_image_name = docker_image.gsub(':latest', '')
|
|
171
176
|
Actions.sh(@docker_registry_login) if @docker_registry_login
|
|
172
177
|
@docker_commander.pull_image(docker_image_name: docker_image_name)
|
|
@@ -183,7 +188,6 @@ module Fastlane
|
|
|
183
188
|
if port_open?('0.0.0.0', @no_vnc_port)
|
|
184
189
|
UI.important('Something went wrong. VNC port is still busy')
|
|
185
190
|
sleep @sleep_interval
|
|
186
|
-
@docker_commander.stop_container
|
|
187
191
|
@docker_commander.delete_container
|
|
188
192
|
end
|
|
189
193
|
end
|
|
@@ -197,10 +201,6 @@ module Fastlane
|
|
|
197
201
|
nil
|
|
198
202
|
end
|
|
199
203
|
|
|
200
|
-
def print_cpu_load(load = cpu_load)
|
|
201
|
-
UI.important("CPU load is: #{load}")
|
|
202
|
-
end
|
|
203
|
-
|
|
204
204
|
# Checks if container is already available
|
|
205
205
|
def container_available?
|
|
206
206
|
return false unless container_name
|
|
@@ -246,6 +246,9 @@ module Fastlane
|
|
|
246
246
|
end
|
|
247
247
|
UI.important("The Container failed to load after '#{timeout}' seconds timeout. Reason: '#{@container.json['State']['Status']}'")
|
|
248
248
|
false
|
|
249
|
+
rescue StandardError => e
|
|
250
|
+
puts e
|
|
251
|
+
false
|
|
249
252
|
end
|
|
250
253
|
|
|
251
254
|
# Checks if port is already openZ
|
|
@@ -257,41 +260,12 @@ module Fastlane
|
|
|
257
260
|
false
|
|
258
261
|
end
|
|
259
262
|
|
|
260
|
-
# Gets load average of Linux machine
|
|
261
|
-
def cpu_load
|
|
262
|
-
load = `cat /proc/loadavg`
|
|
263
|
-
load.split(' ').first.to_f
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
# Gets amount of the CPU cores
|
|
267
|
-
def cpu_core_amount
|
|
268
|
-
`cat /proc/cpuinfo | awk '/^processor/{print $3}' | tail -1`
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# For half an hour waiting until CPU load average is less than number of cores*2 (which considers that CPU is ready)
|
|
272
|
-
# Raises when 30 minutes timeout exceeds
|
|
273
|
-
def wait_cpu_to_idle
|
|
274
|
-
if OS.linux?
|
|
275
|
-
30.times do
|
|
276
|
-
load = cpu_load
|
|
277
|
-
return true if load < cpu_core_amount.to_i * 1.5
|
|
278
|
-
print_cpu_load(load)
|
|
279
|
-
UI.important('Waiting for available resources..')
|
|
280
|
-
sleep 60
|
|
281
|
-
end
|
|
282
|
-
else
|
|
283
|
-
return true
|
|
284
|
-
end
|
|
285
|
-
raise "CPU was overloaded. Couldn't start emulator"
|
|
286
|
-
end
|
|
287
|
-
|
|
288
263
|
# if we do not have container name, we cane use container ID that we got from create call
|
|
289
264
|
def set_container_name(container)
|
|
290
265
|
unless container_name
|
|
291
266
|
@container_name = @emulator_commander.container_name = @docker_commander.container_name = container
|
|
292
267
|
end
|
|
293
268
|
end
|
|
294
|
-
|
|
295
269
|
end
|
|
296
270
|
end
|
|
297
271
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fastlane-plugin-mango
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Serghei Moret, Daniel Hartwich
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-07-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: docker-api
|
|
@@ -146,6 +146,7 @@ files:
|
|
|
146
146
|
- README.md
|
|
147
147
|
- lib/fastlane/plugin/mango.rb
|
|
148
148
|
- lib/fastlane/plugin/mango/actions/run_dockerized_task_action.rb
|
|
149
|
+
- lib/fastlane/plugin/mango/helper/cpu_load_handler.rb
|
|
149
150
|
- lib/fastlane/plugin/mango/helper/docker_commander.rb
|
|
150
151
|
- lib/fastlane/plugin/mango/helper/emulator_commander.rb
|
|
151
152
|
- lib/fastlane/plugin/mango/helper/mango_helper.rb
|
|
@@ -169,8 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
169
170
|
- !ruby/object:Gem::Version
|
|
170
171
|
version: '0'
|
|
171
172
|
requirements: []
|
|
172
|
-
|
|
173
|
-
rubygems_version: 2.7.4
|
|
173
|
+
rubygems_version: 3.0.6
|
|
174
174
|
signing_key:
|
|
175
175
|
specification_version: 4
|
|
176
176
|
summary: This plugin Android tasks on docker images
|