kong/spec/helpers/perf/drivers/terraform.lua (586 lines of code) (raw):

local perf = require("spec.helpers.perf") local pl_path = require("pl.path") local cjson = require("cjson") local tools = require("kong.tools.utils") math.randomseed(os.time()) local _M = {} local mt = {__index = _M} local UPSTREAM_PORT = 8088 local KONG_ADMIN_PORT local PG_PASSWORD = tools.random_string() local KONG_ERROR_LOG_PATH = "/tmp/error.log" local KONG_DEFAULT_HYBRID_CERT = "/tmp/kong-hybrid-cert.pem" local KONG_DEFAULT_HYBRID_CERT_KEY = "/tmp/kong-hybrid-key.pem" -- threshold for load_avg / nproc, not based on specific research, -- just a arbitrary number to ensure test env is normalized local LOAD_NORMALIZED_THRESHOLD = 0.2 function _M.new(opts) local provider = opts and opts.provider or "equinix-metal" local work_dir = "./spec/fixtures/perf/terraform/" .. provider if not pl_path.exists(work_dir) then error("Hosting provider " .. provider .. " unsupported: expect " .. work_dir .. " to exists", 2) end local tfvars = "" if opts and opts.tfvars then for k, v in pairs(opts.tfvars) do tfvars = string.format("%s -var '%s=%s' ", tfvars, k, v) end end local ssh_user = "root" if opts.provider == "aws-ec2" then ssh_user = "ubuntu" end return setmetatable({ opts = opts, log = perf.new_logger("[terraform]"), ssh_log = perf.new_logger("[terraform][ssh]"), provider = provider, work_dir = work_dir, tfvars = tfvars, kong_ip = nil, kong_internal_ip = nil, worker_ip = nil, worker_internal_ip = nil, systemtap_sanity_checked = false, systemtap_dest_path = nil, daily_image_desc = nil, ssh_user = ssh_user, }, mt) end local function ssh_execute_wrap(self, ip, cmd) -- to quote a ', one need to finish the current ', quote the ' then start a new ' cmd = string.gsub(cmd, "'", "'\\''") return "ssh " .. "-o IdentityFile=" .. self.work_dir .. "/id_rsa " .. -- TODO: no hardcode -- timeout is detected 3xServerAliveInterval "-o TCPKeepAlive=yes -o ServerAliveInterval=10 " .. -- turn on connection multiplexing "-o ControlPath=" .. self.work_dir .. "/cm-%r@%h:%p " .. "-o ControlMaster=auto -o ControlPersist=5m " .. -- no interactive prompt for saving hostkey "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " .. -- silence warnings like "Permanently added xxx" "-o LogLevel=ERROR " .. self.ssh_user .. "@" .. ip .. " '" .. cmd .. "'" end -- if remote_ip is set, run remotely; else run on host machine local function execute_batch(self, remote_ip, cmds, continue_on_error) for _, cmd in ipairs(cmds) do if remote_ip then cmd = ssh_execute_wrap(self, remote_ip, cmd) end local _, err = perf.execute(cmd, { logger = (remote_ip and self.ssh_log or self.log).log_exec }) if err then if not continue_on_error then return false, "failed in \"" .. cmd .. "\": ".. (err or "nil") end self.log.warn("execute ", cmd, " has error: ", (err or "nil")) end end return true end function _M:remote_execute(node_type, cmds, continue_on_error) local ip if node_type == "kong" then ip = self.kong_ip elseif node_type == "worker" then ip = self.worker_ip elseif node_type == "db" then ip = self.db_ip else return false, "unknown node type: " .. node_type end return execute_batch(self, ip, cmds, continue_on_error) end function _M:setup(opts) local bin, err = perf.execute("which terraform") if err or #bin == 0 then return nil, "terraform binary not found" end local ok, _ -- terraform apply self.log.info("Running terraform to provision instances...") _, err = execute_batch(self, nil, { "terraform version", "cd " .. self.work_dir .. " && terraform init", "cd " .. self.work_dir .. " && terraform apply -auto-approve " .. self.tfvars, }) if err then return false, err end -- grab outputs local res res, err = perf.execute("cd " .. self.work_dir .. " && terraform output -json") if err then return false, "terraform show: " .. err end res = cjson.decode(res) self.kong_ip = res["kong-ip"].value self.kong_internal_ip = res["kong-internal-ip"].value if self.opts.seperate_db_node then self.db_ip = res["db-ip"].value self.db_internal_ip = res["db-internal-ip"].value else self.db_ip = self.kong_ip self.db_internal_ip = self.kong_internal_ip end self.worker_ip = res["worker-ip"].value self.worker_internal_ip = res["worker-internal-ip"].value -- install psql docker on db instance ok, err = execute_batch(self, self.db_ip, { "sudo apt-get purge unattended-upgrades -y", "sudo apt-get update -qq", "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install -y --force-yes docker.io", "sudo docker rm -f kong-database || true", -- if exist remove it "sudo docker volume rm $(sudo docker volume ls -qf dangling=true) || true", -- cleanup postgres volumes if any "sudo docker run -d -p5432:5432 ".. "-e POSTGRES_PASSWORD=" .. PG_PASSWORD .. " " .. "-e POSTGRES_DB=kong_tests " .. "-e POSTGRES_USER=kong --name=kong-database postgres:13 postgres -N 2333", }) if not ok then return ok, err end -- wait local cmd = ssh_execute_wrap(self, self.db_ip, "sudo docker logs -f kong-database") if not perf.wait_output(cmd, "is ready to accept connections", 5) then return false, "timeout waiting psql to start (5s)" end return true end function _M:teardown(full) self.setup_kong_called = false if full then -- terraform destroy self.log.info("Running terraform to destroy instances...") local ok, err = execute_batch(self, nil, { "terraform version", "cd " .. self.work_dir .. " && terraform init", "cd " .. self.work_dir .. " && terraform destroy -auto-approve " .. self.tfvars, }) if not ok then return false, err end end perf.git_restore() -- otherwise do nothing return true end function _M:start_worker(conf, port_count) conf = conf or "" local listeners = {} for i=1,port_count do listeners[i] = ("listen %d reuseport;"):format(UPSTREAM_PORT+i-1) end listeners = table.concat(listeners, "\n") conf = ngx.encode_base64(([[ worker_processes auto; worker_cpu_affinity auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; worker_rlimit_nofile 20480; events { accept_mutex off; worker_connections 10620; } http { access_log off; server_tokens off; keepalive_requests 10000; tcp_nodelay on; server { %s location =/health { return 200; } location / { return 200 " performancetestperformancetestperformancetestperformancetestperformancetest"; } %s } }]]):format(listeners, conf)):gsub("\n", "") local ok, err = execute_batch(self, self.worker_ip, { "sudo id", "echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor || true", "sudo apt-get purge unattended-upgrades -y", "sudo apt-get update -qq", "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install -y --force-yes nginx gcc make unzip libssl-dev zlib1g-dev", "which wrk || (rm -rf wrk && git clone https://github.com/wg/wrk -b 4.2.0 && cd wrk && make -j$(nproc) WITH_OPENSSL=/usr && sudo cp wrk /usr/local/bin/wrk)", "which wrk2 || (rm -rf wrk2 && git clone https://github.com/giltene/wrk2 && cd wrk2 && make -j$(nproc) && sudo cp wrk /usr/local/bin/wrk2)", "echo " .. conf .. " | base64 -d | sudo tee /etc/nginx/nginx.conf", "sudo nginx -t", "sudo systemctl restart nginx", }) if not ok then return nil, err end local uris = {} for i=1,port_count do uris[i] = "http://" .. self.worker_internal_ip .. ":" .. UPSTREAM_PORT+i-1 end return uris end local function get_admin_port(self, kong_name) kong_name = kong_name or "default" local port, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "sudo cat /etc/kong/" .. kong_name .. ".conf | grep admin_listen | cut -d ':' -f 2 | grep -oP '\\d+' || true")) if port and tonumber(port) then return tonumber(port) else self.log.warn("unable to read admin port for " .. kong_name .. ", fallback to default port " .. KONG_ADMIN_PORT .. ": " .. tostring(err)) return KONG_ADMIN_PORT end end local function prepare_spec_helpers(self, use_git, version) perf.setenv("KONG_PG_HOST", self.db_ip) perf.setenv("KONG_PG_PASSWORD", PG_PASSWORD) -- self.log.debug("(In a low voice) pg_password is " .. PG_PASSWORD) if not use_git then local current_spec_helpers_version = perf.get_kong_version(true) if current_spec_helpers_version ~= version then self.log.info("Current spec helpers version " .. current_spec_helpers_version .. " doesn't match with version to be tested " .. version .. ", checking out remote version") version = version:match("%d+%.%d+%.%d+") perf.git_checkout(version) -- throws end end self.log.info("Infra is up! However, preapring database remotely may take a while...") for i=1, 3 do perf.clear_loaded_package() -- just to let spec.helpers happy, we are not going to start kong locally require("kong.meta")._DEPENDENCIES.nginx = {"0.0.0.0", "9.9.9.9"} local pok, pret = pcall(require, "spec.helpers") package.loaded['kong.meta'] = nil require("kong.meta") if pok then pret.admin_client = function(timeout) return pret.http_client(self.kong_ip, get_admin_port(self), timeout or 60000) end perf.unsetenv("KONG_PG_HOST") perf.unsetenv("KONG_PG_PASSWORD") return pret end self.log.warn("unable to load spec.helpers: " .. (pret or "nil") .. ", try " .. i) ngx.sleep(1) end error("Unable to load spec.helpers") end function _M:setup_kong(version) local ok, err = _M.setup(self) if not ok then return ok, err end local git_repo_path, _ if version:startswith("git:") then git_repo_path = perf.git_checkout(version:sub(#("git:")+1)) version = perf.get_kong_version() self.log.debug("current git hash resolves to Kong version ", version) end local download_path local download_user, download_pass = "x", "x" if version:sub(1, 1) == "2" then download_path = "https://download.konghq.com/gateway-2.x-ubuntu-focal/pool/all/k/kong/kong_" .. version .. "_amd64.deb" else error("Unknown download location for Kong version " .. version) end local docker_extract_cmds self.daily_image_desc = nil -- daily image are only used when testing with git -- testing upon release artifact won't apply daily image files local daily_image = "kong/kong:master-nightly-ubuntu" if self.opts.use_daily_image and git_repo_path then -- install docker on kong instance local _, err = execute_batch(self, self.kong_ip, { "sudo apt-get update -qq", "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install -y --force-yes docker.io", "sudo docker version", }) if err then return false, err end docker_extract_cmds = { "sudo docker rm -f daily || true", "sudo docker rmi -f " .. daily_image, "sudo docker pull " .. daily_image, "sudo docker create --name daily " .. daily_image, "sudo rm -rf /tmp/lua && sudo docker cp daily:/usr/local/share/lua/5.1/. /tmp/lua", -- don't overwrite kong source code, use them from current git repo instead "sudo rm -rf /tmp/lua/kong && sudo cp -r /tmp/lua/. /usr/local/share/lua/5.1/", } for _, dir in ipairs({"/usr/local/openresty", "/usr/local/kong/include", "/usr/local/kong/lib"}) do -- notice the /. it makes sure the content not the directory itself is copied table.insert(docker_extract_cmds, "sudo docker cp daily:" .. dir .."/. " .. dir) end table.insert(docker_extract_cmds, "sudo rm -rf /tmp/lua && sudo docker cp daily:/usr/local/share/lua/5.1/. /tmp/lua") table.insert(docker_extract_cmds, "sudo rm -rf /tmp/lua/kong && sudo cp -r /tmp/lua/. /usr/local/share/lua/5.1/") end local ok, err = execute_batch(self, self.kong_ip, { "sudo apt-get purge unattended-upgrades -y", "sudo apt-get update -qq", "echo | sudo tee " .. KONG_ERROR_LOG_PATH, -- clear it "sudo id", -- set cpu scheduler to performance, it should lock cpufreq to static freq "echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor || true", -- increase outgoing port range to avoid 99: Cannot assign requested address "sudo sysctl net.ipv4.ip_local_port_range='10240 65535'", -- stop and remove kong if installed "dpkg -l kong && (sudo kong stop; sudo dpkg -r kong) || true", -- have to do the pkill sometimes, because kong stop allow the process to linger for a while "sudo pkill -F /usr/local/kong/pids/nginx.pid || true", -- remove all lua files, not only those installed by package "sudo rm -rf /usr/local/share/lua/5.1/kong", "wget -nv " .. download_path .. " --user " .. download_user .. " --password " .. download_pass .. " -O kong-" .. version .. ".deb", "sudo dpkg -i kong-" .. version .. ".deb || sudo apt-get -f -y install", -- generate hybrid cert "kong hybrid gen_cert " .. KONG_DEFAULT_HYBRID_CERT .. " " .. KONG_DEFAULT_HYBRID_CERT_KEY .. " || true", }) if not ok then return false, err end if docker_extract_cmds then _, err = execute_batch(self, self.kong_ip, docker_extract_cmds) if err then return false, "error extracting docker daily image:" .. err end local manifest manifest, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "sudo docker inspect " .. daily_image)) if err then return nil, "failed to inspect daily image: " .. err end local labels labels, err = perf.parse_docker_image_labels(manifest) if err then return nil, "failed to use parse daily image manifest: " .. err end self.log.debug("daily image " .. labels.version .." was pushed at ", labels.created) self.daily_image_desc = labels.version .. ", " .. labels.created end local kong_conf = {} kong_conf["pg_host"] = self.db_internal_ip kong_conf["pg_password"] = PG_PASSWORD kong_conf["pg_database"] = "kong_tests" local kong_conf_blob = "" for k, v in pairs(kong_conf) do kong_conf_blob = string.format("%s\n%s=%s\n", kong_conf_blob, k, v) end kong_conf_blob = ngx.encode_base64(kong_conf_blob):gsub("\n", "") _, err = execute_batch(self, nil, { -- upload git_repo_path and ("(cd " .. git_repo_path .. " && tar zc kong) | " .. ssh_execute_wrap(self, self.kong_ip, "sudo tar zx -C /usr/local/share/lua/5.1")) or "echo use stock files", git_repo_path and (ssh_execute_wrap(self, self.kong_ip, "sudo cp -r /usr/local/share/lua/5.1/kong/include/. /usr/local/kong/include/ && sudo chmod 777 -R /usr/local/kong/include/ || true")) or "echo use stock files", -- run migrations with default configurations ssh_execute_wrap(self, self.kong_ip, "sudo mkdir -p /etc/kong"), ssh_execute_wrap(self, self.kong_ip, "echo " .. kong_conf_blob .. " | base64 -d | sudo tee /etc/kong/kong.conf"), ssh_execute_wrap(self, self.kong_ip, "sudo kong migrations bootstrap"), ssh_execute_wrap(self, self.kong_ip, "sudo kong migrations up -y || true"), ssh_execute_wrap(self, self.kong_ip, "sudo kong migrations finish -y || true"), }) if err then return false, err end self.setup_kong_called = true return prepare_spec_helpers(self, git_repo_path, version) end function _M:start_kong(kong_conf, driver_conf) if not self.setup_kong_called then return false, "setup_kong() must be called before start_kong()" end local kong_name = driver_conf and driver_conf.name or "default" local prefix = "/usr/local/kong_" .. kong_name local conf_path = "/etc/kong/" .. kong_name .. ".conf" kong_conf = kong_conf or {} kong_conf["prefix"] = kong_conf["prefix"] or prefix kong_conf["pg_host"] = kong_conf["pg_host"] or self.db_internal_ip kong_conf["pg_password"] = kong_conf["pg_password"] or PG_PASSWORD kong_conf["pg_database"] = kong_conf["pg_database"] or "kong_tests" kong_conf['proxy_access_log'] = kong_conf['proxy_access_log'] or "/dev/null" kong_conf['proxy_error_log'] = kong_conf['proxy_error_log'] or KONG_ERROR_LOG_PATH kong_conf['admin_error_log'] = kong_conf['admin_error_log'] or KONG_ERROR_LOG_PATH KONG_ADMIN_PORT = 39001 kong_conf['admin_listen'] = kong_conf['admin_listen'] or ("0.0.0.0:" .. KONG_ADMIN_PORT) kong_conf['vitals'] = kong_conf['vitals'] or "off" kong_conf['anonymous_reports'] = kong_conf['anonymous_reports'] or "off" if not kong_conf['cluster_cert'] then kong_conf['cluster_cert'] = KONG_DEFAULT_HYBRID_CERT kong_conf['cluster_cert_key'] = KONG_DEFAULT_HYBRID_CERT_KEY end local kong_conf_blob = "" for k, v in pairs(kong_conf) do kong_conf_blob = string.format("%s\n%s=%s\n", kong_conf_blob, k, v) end kong_conf_blob = ngx.encode_base64(kong_conf_blob):gsub("\n", "") local _, err = execute_batch(self, self.kong_ip, { "mkdir -p /etc/kong || true", "echo " .. kong_conf_blob .. " | base64 -d | sudo tee " .. conf_path, "sudo rm -rf " .. prefix .. " && sudo mkdir -p " .. prefix .. " && sudo chown kong:kong -R " .. prefix, "sudo kong check " .. conf_path, string.format("sudo kong migrations up -y -c %s || true", conf_path), string.format("sudo kong migrations finish -y -c %s || true", conf_path), string.format("ulimit -n 655360; sudo kong start -c %s || sudo kong restart -c %s", conf_path, conf_path), -- set mapping of kong name to IP for use like Hybrid mode "grep -q 'START PERF HOSTS' /etc/hosts || (echo '## START PERF HOSTS' | sudo tee -a /etc/hosts)", "echo " .. self.kong_internal_ip .. " " .. kong_name .. " | sudo tee -a /etc/hosts", }) if err then return false, err end return true end function _M:stop_kong() local load, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "cat /proc/loadavg")) if err then self.log.err("failed to get loadavg: " .. err) end self.log.debug("Kong node end 1m loadavg is ", load:match("[%d%.]+")) return execute_batch(self, self.kong_ip, { "sudo pkill -kill nginx", "sudo sed '/START PERF HOSTS/Q' -i /etc/hosts", }) end function _M:get_start_load_cmd(stub, script, uri) if not uri then uri = string.format("http://%s:8000", self.kong_internal_ip) end local script_path if script then script_path = string.format("/tmp/wrk-%s.lua", tools.random_string()) local out, err = perf.execute( ssh_execute_wrap(self, self.worker_ip, "tee " .. script_path), { stdin = script, }) if err then return false, "failed to write script in remote machine: " .. (out or err) end end script_path = script_path and ("-s " .. script_path) or "" local nproc, err nproc, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "nproc")) if not nproc or err then return false, "failed to get nproc: " .. (err or "") end if not tonumber(nproc) then return false, "failed to get nproc: " .. (nproc or "") end nproc = tonumber(nproc) local loadavg while true do loadavg, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "cat /proc/loadavg")) if not loadavg or err then self.log.err("failed to get loadavg: ", (err or "")) goto continue end loadavg = loadavg:match("[%d%.]+") if not loadavg or not tonumber(loadavg) then self.log.err("failed to get loadavg: ", loadavg or "nil") goto continue end loadavg = tonumber(loadavg) local load_normalized = loadavg / nproc if load_normalized < LOAD_NORMALIZED_THRESHOLD then break end self.log.info("waiting for Kong node 1m loadavg to drop under ", nproc * LOAD_NORMALIZED_THRESHOLD, ", now: ", loadavg) ngx.sleep(15) ::continue:: end self.log.debug("Kong node start 1m loadavg is ", loadavg) return ssh_execute_wrap(self, self.worker_ip, stub:format(script_path, uri)) end function _M:get_admin_uri(kong_name) return string.format("http://%s:%s", self.kong_internal_ip, get_admin_port(self, kong_name)) end local function check_systemtap_sanity(self) local _, err _, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "which stap")) if err then _, err = execute_batch(self, self.kong_ip, { "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install g++ libelf-dev libdw-dev libssl-dev libsqlite3-dev libnss3-dev pkg-config python3 make -y --force-yes", "wget https://sourceware.org/systemtap/ftp/releases/systemtap-4.6.tar.gz -O systemtap.tar.gz", "tar xf systemtap.tar.gz", "cd systemtap-*/ && " .. "./configure --enable-sqlite --enable-bpf --enable-nls --enable-nss --enable-avahi && " .. "make PREFIX=/usr -j$(nproc) && ".. "sudo make install" }) if err then return false, "failed to build systemtap: " .. err end end _, err = execute_batch(self, self.kong_ip, { "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install gcc linux-headers-$(uname -r) -y --force-yes", "which stap", "stat /tmp/stapxx || git clone https://github.com/Kong/stapxx /tmp/stapxx", "stat /tmp/perf-ost || git clone https://github.com/openresty/openresty-systemtap-toolkit /tmp/perf-ost", "stat /tmp/perf-fg || git clone https://github.com/brendangregg/FlameGraph /tmp/perf-fg" }) if err then return false, err end -- try compile the kernel module local out out, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "sudo stap -ve 'probe begin { print(\"hello\\n\"); exit();}'")) if err then return nil, "systemtap failed to compile kernel module: " .. (out or "nil") .. " err: " .. (err or "nil") .. "\n Did you install gcc and kernel headers?" end return true end function _M:get_start_stapxx_cmd(sample, args, driver_conf) if not self.systemtap_sanity_checked then local ok, err = check_systemtap_sanity(self) if not ok then return nil, err end self.systemtap_sanity_checked = true end -- find one of kong's child process hopefully it's a worker -- (does kong have cache loader/manager?) local kong_name = driver_conf and driver_conf.name or "default" local prefix = "/usr/local/kong_" .. kong_name local pid, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "pid=$(cat " .. prefix .. "/pids/nginx.pid); " .. "cat /proc/$pid/task/$pid/children | awk '{print $1}'")) if err or not tonumber(pid) then return nil, "failed to get Kong worker PID: " .. (err or "nil") end self.systemtap_dest_path = "/tmp/" .. tools.random_string() return ssh_execute_wrap(self, self.kong_ip, "sudo /tmp/stapxx/stap++ /tmp/stapxx/samples/" .. sample .. " --skip-badvars -D MAXSKIPPED=1000000 -x " .. pid .. " " .. args .. " > " .. self.systemtap_dest_path .. ".bt" ) end function _M:get_wait_stapxx_cmd(timeout) return ssh_execute_wrap(self, self.kong_ip, "lsmod | grep stap_") end function _M:generate_flamegraph(title, opts) local path = self.systemtap_dest_path self.systemtap_dest_path = nil local out, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "cat " .. path .. ".bt")) if err or #out == 0 then return nil, "systemtap output is empty, possibly no sample are captured" end local ok, err = execute_batch(self, self.kong_ip, { "/tmp/perf-ost/fix-lua-bt " .. path .. ".bt > " .. path .. ".fbt", "/tmp/perf-fg/stackcollapse-stap.pl " .. path .. ".fbt > " .. path .. ".cbt", "/tmp/perf-fg/flamegraph.pl --title='" .. title .. "' " .. (opts or "") .. " " .. path .. ".cbt > " .. path .. ".svg", }) if not ok then return false, err end local out, _ = perf.execute(ssh_execute_wrap(self, self.kong_ip, "cat " .. path .. ".svg")) perf.execute(ssh_execute_wrap(self, self.kong_ip, "sudo rm -v " .. path .. ".*"), { logger = self.ssh_log.log_exec }) return out end function _M:save_error_log(path) return perf.execute(ssh_execute_wrap(self, self.kong_ip, "cat " .. KONG_ERROR_LOG_PATH) .. " >'" .. path .. "'", { logger = self.ssh_log.log_exec }) end function _M:save_pgdump(path) return perf.execute(ssh_execute_wrap(self, self.kong_ip, "sudo docker exec -i kong-database psql -Ukong kong_tests --data-only") .. " >'" .. path .. "'", { logger = self.ssh_log.log_exec }) end function _M:load_pgdump(path, dont_patch_service) local _, err = perf.execute("cat " .. path .. "| " .. ssh_execute_wrap(self, self.kong_ip, "sudo docker exec -i kong-database psql -Ukong kong_tests"), { logger = self.ssh_log.log_exec }) if err then return false, err end if dont_patch_service then return true end return perf.execute("echo \"UPDATE services set host='" .. self.worker_ip .. "', port=" .. UPSTREAM_PORT .. ", protocol='http';\" | " .. ssh_execute_wrap(self, self.kong_ip, "sudo docker exec -i kong-database psql -Ukong kong_tests"), { logger = self.ssh_log.log_exec }) end function _M:get_based_version() return self.daily_image_desc or perf.get_kong_version() end return _M