diff --git a/.github/workflows/test_on_push.yaml b/.github/workflows/test_on_push.yaml new file mode 100644 index 00000000..2be7efe2 --- /dev/null +++ b/.github/workflows/test_on_push.yaml @@ -0,0 +1,39 @@ +name: Run tests + +on: + push: + pull_request: + +jobs: + run-tests-ce: + if: | + github.event_name == 'push' || + github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != 'tarantool' + strategy: + matrix: + tarantool-version: ["1.10", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7"] + fail-fast: false + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@master + + - name: Install requirements for community + run: | + curl -L https://tarantool.io/installer.sh | sudo VER=${{ matrix.tarantool-version }} bash + sudo apt install -y tarantool-dev + tarantool --version + ./deps.sh + + # This server starts and listen on 8084 port that is used for tests + - name: Stop Mono server + run: sudo kill -9 $(sudo lsof -t -i tcp:8084) || true + +# - name: Run linter +# run: .rocks/bin/luacheck . + + - name: Run luatest tests + run: .rocks/bin/luatest -v + + - name: Run tap tests + run: make test + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3ebefcea..00000000 --- a/.travis.yml +++ /dev/null @@ -1,175 +0,0 @@ -sudo: false -language: C -services: - - docker - -cache: - directories: - - $HOME/.cache - -env: - global: - - PRODUCT=tarantool-expirationd - matrix: - - TARGET=test VERSION=1_10 - - TARGET=test VERSION=2x - - TARGET=test VERSION=2_2 - - TARGET=test VERSION=2_3 - - TARGET=test VERSION=2_4 - - OS=el DIST=6 - - OS=el DIST=7 - - OS=el DIST=8 - - OS=fedora DIST=28 - - OS=fedora DIST=29 - - OS=fedora DIST=30 - - OS=fedora DIST=31 - - OS=ubuntu DIST=trusty - - OS=ubuntu DIST=xenial - - OS=ubuntu DIST=bionic - - OS=ubuntu DIST=focal - - OS=debian DIST=jessie - - OS=debian DIST=stretch - - OS=debian DIST=buster - -script: - - git describe --long - - | - if [ "${TARGET}" = "test" ]; then - ./test.sh; - else - git clone https://github.com/packpack/packpack.git packpack; - packpack/packpack; - fi; - -before_deploy: - - ls -l build/ - -deploy: - # Deploy packages to PackageCloud from master branch - - provider: packagecloud - username: tarantool - repository: "1_10" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - branch: master - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2x" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - branch: master - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2_2" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - branch: master - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2_3" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - branch: master - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2_4" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - branch: master - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - # Deploy packages to PackageCloud from tags - # see: - # * https://github.com/tarantool/tarantool/issues/3745 - # * https://github.com/travis-ci/travis-ci/issues/7780#issuecomment-302389370 - - provider: packagecloud - username: tarantool - repository: "1_10" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - tags: true - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2x" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - tags: true - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2_2" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - tags: true - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2_3" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - tags: true - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - - provider: packagecloud - username: tarantool - repository: "2_4" - token: ${PACKAGECLOUD_TOKEN} - dist: ${OS}/${DIST} - package_glob: build/*.{rpm,deb,dsc} - skip_cleanup: true - on: - tags: true - condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - - # Archive packages - # - provider: script - # script: ./travis-archive.sh modules - # on: - # branch: master - # condition: -n "${OS}" && -n "${DIST}" && -n "${PACKAGECLOUD_TOKEN}" && "${TRAVIS_EVENT_TYPE}" != 'cron' - -notifications: - email: - recipients: - - build@tarantool.org - on_success: change - on_failure: always diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 7cdcde97..00000000 --- a/Jenkinsfile +++ /dev/null @@ -1,8 +0,0 @@ -stage('Build'){ - packpack = new org.tarantool.packpack() - node { - checkout scm - packpack.prepareSources() - } - packpack.packpackBuildMatrix('result') -} diff --git a/debian/.gitignore b/debian/.gitignore deleted file mode 100644 index 4e408d95..00000000 --- a/debian/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -tarantool-expirationd/ -files -stamp-* -*.substvars -*.log diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 7852052e..00000000 --- a/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -tarantool-expirationd (1.0.0-1) unstable; urgency=medium - - * Initial release - - -- Roman Tsisyk Thu, 18 Feb 2016 10:11:03 +0300 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index ec635144..00000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/debian/control b/debian/control deleted file mode 100644 index 26fc84e0..00000000 --- a/debian/control +++ /dev/null @@ -1,25 +0,0 @@ -Source: tarantool-expirationd -Priority: optional -Section: database -Maintainer: Roman Tsisyk -Build-Depends: debhelper (>= 9), - tarantool (>= 1.6.8.0), -# For /usr/bin/prove - perl (>= 5.10.0) -Standards-Version: 3.9.6 -Homepage: https://github.com/tarantool/expirationd -Vcs-Git: git://github.com/tarantool/expirationd.git -Vcs-Browser: https://github.com/tarantool/expirationd - -Package: tarantool-expirationd -Architecture: all -Depends: tarantool (>= 1.6.8.0), ${misc:Depends} -Description: Expiration daemon for Tarantool - This package can turn Tarantool into a persistent memcache replacement, - but is powerful enough so that your own expiration strategy can be defined. - . - You define two functions: one takes a tuple as an input and returns true in - case it's expirted and false otherwise. The other takes the tuple and - performs the expiry itself: either deletes it (memcache), or does something - smarter, like put a smaller representation of the data being deleted into - some other space. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index e7a89bc1..00000000 --- a/debian/copyright +++ /dev/null @@ -1,29 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Debianized-By: Roman Tsisyk -Upstream-Name: tarantool-expirationd -Upstream-Contact: support@tarantool.org -Source: https://github.com/tarantool/tarantool-expirationd - -Files: * -Copyright: 2014-2016 Tarantool AUTHORS -License: BSD-2-Clause - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - . - THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. diff --git a/debian/docs b/debian/docs deleted file mode 100644 index b43bf86b..00000000 --- a/debian/docs +++ /dev/null @@ -1 +0,0 @@ -README.md diff --git a/debian/rules b/debian/rules deleted file mode 100755 index 2d33f6ac..00000000 --- a/debian/rules +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/make -f - -%: - dh $@ diff --git a/debian/source/format b/debian/source/format deleted file mode 100644 index 163aaf8d..00000000 --- a/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/debian/tarantool-expirationd.install b/debian/tarantool-expirationd.install deleted file mode 100644 index 8e2c7b74..00000000 --- a/debian/tarantool-expirationd.install +++ /dev/null @@ -1 +0,0 @@ -expirationd.lua usr/share/tarantool/ diff --git a/deps.sh b/deps.sh new file mode 100755 index 00000000..56b76335 --- /dev/null +++ b/deps.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Call this script to install test dependencies + +set -e + +# Test dependencies: +tarantoolctl rocks install luatest 0.5.0 +tarantoolctl rocks install luacheck 0.25.0 + +tarantoolctl rocks install cartridge 2.5.1 diff --git a/expirationd.lua b/expirationd.lua index 625331a7..a470b934 100644 --- a/expirationd.lua +++ b/expirationd.lua @@ -56,6 +56,7 @@ local constants = { -- Task fibers -- ------------------------------------------------------------------------- -- +-- get all fields in primary key(composite possible) from tuple local function construct_key(space_id, tuple) return fun.map( function(x) return tuple[x.fieldno] end, @@ -63,6 +64,7 @@ local function construct_key(space_id, tuple) ):totable() end +-- do expiration process on tuple local function expiration_process(task, tuple) task.checked_tuples_count = task.checked_tuples_count + 1 if task.is_tuple_expired(task.args, tuple) then @@ -71,6 +73,7 @@ local function expiration_process(task, tuple) end end +-- stop for some time local function suspend_basic(scan_space, task, len) local delay = (task.tuples_per_iteration * task.full_scan_time) delay = math.min(delay / len, task.iteration_delay) @@ -78,12 +81,14 @@ local function suspend_basic(scan_space, task, len) end local function suspend(scan_space, task) + -- Return the number of tuples in the space local space_len = scan_space:len() if space_len > 0 then suspend_basic(scan_space, task, space_len) end end +-- delete with some suspend and some tuples limit local function tree_index_iter(scan_space, task) -- iteration with GT iterator local params = {iterator = 'GT', limit = task.tuples_per_iteration} @@ -96,6 +101,7 @@ local function tree_index_iter(scan_space, task) end suspend(scan_space, task) local key = construct_key(scan_space.id, last_id) + -- select all greater then last key tuples = scan_space.index[0]:select(key, params) end @@ -186,6 +192,7 @@ local function guardian_loop(task) fiber.name(string.format("guardian of %q", task.name), { truncate = true }) while true do + -- if fiber doesn't exist if get_fiber_id(task.worker_fiber) == 0 then -- create worker fiber task.worker_fiber = fiber.create(worker_loop, task) @@ -205,7 +212,7 @@ end -- * task:start() -- start task -- * task:stop() -- stop task -- * task:restart() -- restart task --- * task:kill() -- delete task and restart +-- * task:kill() -- stop task and delete -- * task:statistics() -- return table with statistics local Task_methods = { start = function (self) @@ -502,10 +509,7 @@ local function expirationd_task_stats(name) return retval end --- kill task -local function expirationd_kill_task(name) - return get_task(name):kill() -end + -- get task by name local function expirationd_get_task(name) diff --git a/rpm/tarantool-expirationd.spec b/rpm/tarantool-expirationd.spec deleted file mode 100644 index 1d1bcfff..00000000 --- a/rpm/tarantool-expirationd.spec +++ /dev/null @@ -1,41 +0,0 @@ -Name: tarantool-expirationd -Version: 1.0.0 -Release: 1%{?dist} -Summary: Expiration daemon for Tarantool -Group: Applications/Databases -License: BSD -URL: https://github.com/tarantool/tarantool-expirationd -Source0: https://github.com/tarantool/%{name}/archive/%{version}/%{name}-%{version}.tar.gz -BuildArch: noarch -BuildRequires: tarantool >= 1.6.8.0 -BuildRequires: /usr/bin/prove -Requires: tarantool >= 1.6.8.0 -%description -This package can turn Tarantool into a persistent memcache replacement, -but is powerful enough so that your own expiration strategy can be defined. - -You define two functions: one takes a tuple as an input and returns true in -case it's expirted and false otherwise. The other takes the tuple and -performs the expiry itself: either deletes it (memcache), or does something -smarter, like put a smaller representation of the data being deleted into -some other space. - -%prep -%setup -q -n %{name}-%{version} - -%check -make test - -%install -install -d %{buildroot}%{_datarootdir}/tarantool/ -install -m 0644 expirationd.lua %{buildroot}%{_datarootdir}/tarantool/ - -%files -%{_datarootdir}/tarantool/expirationd.lua -%doc README.md -%{!?_licensedir:%global license %doc} -%license LICENSE - -%changelog -* Thu Jun 18 2015 Roman Tsisyk 1.0.0-1 -- Initial version of the RPM spec diff --git a/test.sh b/test.sh deleted file mode 100755 index 34381db5..00000000 --- a/test.sh +++ /dev/null @@ -1,15 +0,0 @@ -VERSION=${VERSION:-1_7} - -curl https://packagecloud.io/tarantool/${VERSION}/gpgkey | sudo apt-key add - -release=`lsb_release -c -s` - -sudo apt-get install -y apt-transport-https - -sudo tee /etc/apt/sources.list.d/tarantool_${VERSION}.list <<- EOF -deb https://packagecloud.io/tarantool/${VERSION}/ubuntu/ $release main -deb-src https://packagecloud.io/tarantool/${VERSION}/ubuntu/ $release main -EOF - -sudo apt-get update > /dev/null -sudo apt-get -q -y install tarantool --force-yes -make test diff --git a/test/entrypoint/srv_basic.lua b/test/entrypoint/srv_basic.lua new file mode 100755 index 00000000..2723ea69 --- /dev/null +++ b/test/entrypoint/srv_basic.lua @@ -0,0 +1,46 @@ +#!/usr/bin/env tarantool + +require('strict').on() + +local log = require('log') +local errors = require('errors') +local cartridge = require('cartridge') +errors.set_deprecation_handler(function(err) + log.error('%s', err) + os.exit(1) +end) + +package.preload['mymodule'] = function() + + return { + role_name = 'myrole', + validate_config = function() + return true + end, + init = function(opts) end, + apply_config = function(_, opts) end, + stop = function() end, + } +end + + + +local ok, err = errors.pcall('CartridgeCfgError', cartridge.cfg, { + advertise_uri = 'localhost:3301', + http_port = 8081, + bucket_count = 3000, + roles = { + 'mymodule', + }, + roles_reload_allowed = true, + -- Compatibility tests run on cartridge 1.2.0 + -- which doesn't support it yet. + upload_prefix = package.loaded['cartridge.upload'] and '../upload', +}) +if not ok then + log.error('%s', err) + os.exit(1) +end + +_G.is_initialized = cartridge.is_healthy + diff --git a/test/helper.lua b/test/helper.lua new file mode 100644 index 00000000..222970da --- /dev/null +++ b/test/helper.lua @@ -0,0 +1,30 @@ +require('strict').on() + +local fio = require('fio') +local digest = require('digest') +local helpers = table.copy(require('cartridge.test-helpers')) + +helpers.project_root = fio.dirname(debug.sourcedir()) + +fio.tempdir = function(base) + base = base or os.getenv('TMPDIR') or '/tmp' + local random = digest.urandom(9) + local suffix = digest.base64_encode(random, {urlsafe = true}) + local path = fio.pathjoin(base, 'tmp.cartridge.' .. suffix) + fio.mktree(path) + return path +end + +function helpers.entrypoint(name) + local path = fio.pathjoin( + helpers.project_root, + 'test', 'entrypoint', + string.format('%s.lua', name) + ) + if not fio.path.exists(path) then + error(path .. ': no such entrypoint', 2) + end + return path +end + +return helpers diff --git a/test/integration/hotreload_test.lua b/test/integration/hotreload_test.lua new file mode 100644 index 00000000..d77c3f30 --- /dev/null +++ b/test/integration/hotreload_test.lua @@ -0,0 +1,98 @@ +local fio = require('fio') +local utils = require('cartridge.utils') +local t = require('luatest') +local g = t.group("hot_reload") + +local helpers = require('test.helper') + +local function reload_myrole(fn) + -- For the sake of string.dump() function must have no upvalues. + -- https://www.lua.org/manual/5.1/manual.html#pdf-string.dump + utils.assert_upvalues(fn, {}) + + local ok, err = g.srv.net_box:eval([[ + package.preload["mymodule"] = loadstring(...) + return require("cartridge.roles").reload() + ]], {string.dump(fn)}) + + t.assert_equals({ok, err}, {true, nil}) +end + +g.before_all(function() + local tempdir = fio.tempdir() + g.cluster = helpers.Cluster:new({ + datadir = tempdir, + server_command = helpers.entrypoint('srv_basic'), + cookie = "secret-cluster-cookie", + replicasets = {{ + alias = 'A', + roles = {'myrole'}, + servers = 1, + }}, + }) + g.srv = g.cluster:server('A-1') + g.cluster:start() + + local ok, err = g.srv.net_box:eval([[ + local a = box.schema.create_space('origin', { + if_not_exists = true + }) + a:create_index('pri') + return true, nil + ]]) + t.assert_equals({ok, err}, {true, nil}) +end) + +g.after_all(function() + g.cluster:stop() + fio.rmtree(g.cluster.datadir) +end) + +function g.test_reload_config() + reload_myrole(function() + return { + role_name = 'myrole', + init = function() + rawset(_G, "expirationd", require('expirationd')) + box.space.origin:insert({1}) + local function is_expired(args, tuple) + return true + end + expirationd.start("clean_all", box.space.origin.id, is_expired, {force = true}) + return true, nil + end, + stop = function() + for _, task in pairs(expirationd.tasks()) do + expirationd.kill(task) + end + box.space.origin:insert({2}) + end + } + end) + t.assert_equals( + g.srv.net_box:eval('return box.space.origin:select()'), + {} + ) + reload_myrole(function() + return { + role_name = 'myrole', + init = function() + rawset(_G, "expirationd", require('expirationd')) + local function is_expired(args, tuple) + return true + end + expirationd.start("clean_all", box.space.origin.id, is_expired) + return true, nil + end, + stop = function() + for _, task in pairs(expirationd.tasks()) do + expirationd.kill(task) + end + end + } + end) + t.assert_equals( + g.srv.net_box:eval('return box.space.origin:select()'), + {{ 2 }} + ) +end diff --git a/travis-archive.bash b/travis-archive.bash deleted file mode 100644 index 5bf2822a..00000000 --- a/travis-archive.bash +++ /dev/null @@ -1,55 +0,0 @@ -BIN_DIR="${HOME}/.cache/bin" -MINIO_CLIENT="${BIN_DIR}/mc" - -START_TIME=`date +%s` - -if [[ -n "${OS}" && -n "${DIST}" ]]; then - echo "Uploader: build data isn't found, skipping" - exit 0 -fi - -if [[ ! -x "${MINIO_CLIENT}" ]]; then - echo "Uploader: couldn't find minio client - downloading" - mkdir -p "${BIN_DIR}" - rm -rf "${MINIO_CLIENT}" - wget https://dl.minio.io/server/minio/release/linux-amd64/minio -O ${MINIO_CLIENT} - chmod +x ${MINIO_CLIENT} -fi - -${MINIO_CLIENT} config host add tarantool-s3 http://s3.tarantool.org ${S3_ACCESS_KEY} ${S3_SECRET_KEY} - -DEB_DST="tarantool-s3/archive/${OS}/${DIST}/os/x86_64/" -RPM_DST="tarantool-s3/archive/${OS}/pool/main/${PROJECT}" - -COUNT=0 - -for fullname in "$@"; do - PACKAGE_NAME=`basename "$fullname"` - PACKAGE_EXTENSION="${filename##*.}" - echo -n "Uploader: uploading file $fullname " - - case "${OS}" in - "el") ;; - "fedora") - echo "to ${DEB_DST}" - ${MINIO_CLIENT} mb ${DEB_DST} - ${MINIO_CLIENT} cp $fullname ${DEB_DST} - ;& - "ubuntu") ;; - "debian") - echo "to ${RPM_DST}" - ${MINIO_CLIENT} mb ${RPM_DST} - ${MINIO_CLIENT} cp $fullname ${RPM_DST} - ;& - *) - echo - echo "Uploader: unknown OS '${OS}'" - exit 1 - ;; - esac - COUNT=$((COUNT+1)) -done - -RUN_TIME=$((START_TIME-`date +%s`)) - -echo "Finished uploading $COUNT in ${RUN_TIME} sec"