class Gitlab::QA::Component::Base
def add_network_alias(name)
def add_network_alias(name) @network_aliases.push(name) end
def assert_name!
def assert_name! raise 'Invalid instance name!' unless name end
def hostname
def hostname "#{name}.#{network}" end
def image
def image return self.class.const_get(:DOCKER_IMAGE) if self.class.const_defined?(:DOCKER_IMAGE) raise NotImplementedError, "#{self.class.name} must specify a docker image as DOCKER_IMAGE" end
def initialize
def initialize @docker = Docker::Engine.new @logger = Runtime::Logger.logger @environment = {} @volumes = {} @ports = [] @network_aliases = [] @exec_commands = [] @additional_hosts = [] @secrets = [] end
def instance(skip_teardown: false)
def instance(skip_teardown: false) instance_no_teardown do yield self if block_given? end ensure teardown unless skip_teardown end
def instance_no_teardown # rubocop:disable Metrics/AbcSize
def instance_no_teardown # rubocop:disable Metrics/AbcSize begin retries ||= 0 prepare start reconfigure wait_until_ready process_exec_commands rescue Support::ShellCommand::StatusError => e reconfigure_log_file = get_reconfigure_log_file_from_artefact # for scenarios where a service fails during startup, attempt to retry to avoid flaky failures if (retries += 1) < 3 unless reconfigure_log_file.nil? Runtime::Logger.info( "Follow the document " \ "https://gitlab.com/gitlab-org/quality/runbooks/-/blob/main/debug_orchestrated_test_locally/ " \ "for debugging the test failure locally.") # Tailing the reconfigure logs after retries are over and before raising exception Runtime::Logger.info("Tail of the reconfigure log file, see artifacts for full log: #{reconfigure_log_file}") Support::ShellCommand.new("tail -n 100 #{reconfigure_log_file}", stream_output: true).execute! end Runtime::Logger.warn( "Retry instance_no_teardown due to Support::ShellCommand::StatusError -- attempt #{retries}" ) teardown! retry end # Printing logs to stdout for last retry failure if !reconfigure_log_file.nil? && retries == 3 # Tailing the reconfigure logs after retries are over and before raising exception Runtime::Logger.info("Tail of the reconfigure log file, see artifacts for full log: #{reconfigure_log_file}") Support::ShellCommand.new("tail -n 100 #{reconfigure_log_file}", stream_output: true).execute! end raise e end yield self if block_given? end
def ip_address
def ip_address docker.inspect(name) { |command| command << "-f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'" } end
def log_volume
def log_volume @log_volume ||= { src: File.join(Runtime::Env.host_artifacts_dir, name, 'logs'), dest: '/var/log/gitlab' } end
def name
def name raise NotImplementedError, "#{self.class.name} must specify a default name" end
def prepare
def prepare prepare_docker_image prepare_docker_container prepare_network end
def prepare_airgapped_network
def prepare_airgapped_network return unless airgapped_network && !docker.network_exists?(network) docker.network_create("--driver=bridge --internal #{network}") end
def prepare_docker_container
def prepare_docker_container return unless docker.container_exists?(name) docker.remove(name) end
def prepare_docker_image
def prepare_docker_image pull end
def prepare_network
def prepare_network prepare_airgapped_network prepare_runner_network return if docker.network_exists?(network) docker.network_create(network) end
def prepare_runner_network
def prepare_runner_network return unless runner_network && !docker.network_exists?(runner_network) docker.network_create("--driver=bridge --internal #{runner_network}") end
def process_exec_commands
def process_exec_commands exec_commands.each { |command| docker.exec(name, command) } end
def pull
def pull return if Runtime::Env.skip_pull? docker.pull(image: image, tag: tag) end
def restart
def restart assert_name! docker.restart(name) end
def start # rubocop:disable Metrics/AbcSize
def start # rubocop:disable Metrics/AbcSize docker.run(image: image, tag: tag, mask_secrets: secrets) do |command| command << "-d" command << "--name #{name}" command << "--net #{network}" command << "--hostname #{hostname}" @ports.each do |mapping| command.port(mapping) end @volumes.to_h.each do |to, from| command.volume(to, from, 'Z') end command.volume(*log_volume.values) unless log_volume.empty? @environment.to_h.each do |key, value| command.env(key, value) end @network_aliases.to_a.each do |network_alias| command << "--network-alias #{network_alias}" end @additional_hosts.each do |host| command << "--add-host=#{host}" end end end
def start_instance
def start_instance instance_no_teardown end
def tag
def tag return self.class.const_get(:DOCKER_IMAGE_TAG) if self.class.const_defined?(:DOCKER_IMAGE_TAG) raise NotImplementedError, "#{self.class.name} must specify a docker image tag as DOCKER_IMAGE_TAG" end
def teardown
def teardown unless teardown? Runtime::Logger.info("The orchestrated docker containers have not been removed.") docker.ps return end teardown! end
def teardown!
def teardown! assert_name! return unless docker.running?(name) docker.remove(name) end
def teardown?
def teardown? !Runtime::Scenario.attributes.include?(:teardown) || Runtime::Scenario.teardown end