You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by av...@apache.org on 2021/01/26 07:21:10 UTC

[ignite] branch ignite-ducktape updated: IGNITE-14046 Update ducktape version to 0.8.2 in ignite-ducktape module (#8699)

This is an automated email from the ASF dual-hosted git repository.

av pushed a commit to branch ignite-ducktape
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/ignite-ducktape by this push:
     new 6fd2eb2  IGNITE-14046 Update ducktape version to 0.8.2 in ignite-ducktape module (#8699)
6fd2eb2 is described below

commit 6fd2eb2adab2dec050b7afc3ab0da6a72c8abbd7
Author: Ivan Daschinskiy <iv...@gmail.com>
AuthorDate: Tue Jan 26 10:19:33 2021 +0300

    IGNITE-14046 Update ducktape version to 0.8.2 in ignite-ducktape module (#8699)
---
 .travis.yml                                        |   6 --
 modules/ducktests/tests/README.md                  |   6 +-
 modules/ducktests/tests/docker/requirements.txt    |   2 +-
 modules/ducktests/tests/docker/run_tests.sh        |  17 ++--
 .../ducktests/tests/ignitetest/services/ignite.py  |   2 +-
 .../tests/ignitetest/services/ignite_app.py        |   2 +-
 .../ducktests/tests/ignitetest/services/spark.py   |  14 +--
 .../ignitetest/services/utils/background_thread.py | 113 +++++++++++++++++++++
 .../ignitetest/services/utils/ignite_aware.py      |  30 +++---
 .../tests/ignitetest/services/utils/jmx_utils.py   |   3 +-
 .../tests/ignitetest/services/utils/jvm_utils.py   |  13 +--
 .../tests/ignitetest/services/zk/zookeeper.py      |  10 +-
 modules/ducktests/tests/setup.py                   |   7 +-
 modules/ducktests/tests/tox.ini                    |   3 +-
 14 files changed, 163 insertions(+), 65 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index c2dbd30..205be3a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -56,12 +56,6 @@ matrix:
         - dotnet build modules/platforms/dotnet/Apache.Ignite.DotNetCore.sln
 
     - language: python
-      python: 3.6.12
-      <<: *ducktape-tox
-      script:
-        - tox -e py36
-
-    - language: python
       python: 3.7.9
       <<: *ducktape-tox
       script:
diff --git a/modules/ducktests/tests/README.md b/modules/ducktests/tests/README.md
index fe9f64f..6a839a6 100644
--- a/modules/ducktests/tests/README.md
+++ b/modules/ducktests/tests/README.md
@@ -16,7 +16,7 @@ a running node.
 
 ## Requirements
 To just start tests locally the only requirement is preinstalled `docker`. 
-For development process requirements are `python` >= 3.6.
+For development process requirements are `python` >= 3.7.
 
 ## Run tests locally
 - Change a current directory to`${IGNITE_HOME}`
@@ -55,7 +55,7 @@ All commits and PR's are checked against multiple python's version, namely 3.6,
 If you want to check your PR as it will be checked on Travis CI, you should do following steps:
 
 - Install `pyenv`, see installation instruction [here](https://github.com/pyenv/pyenv#installation).
-- Install different versions of python (recommended versions are `3.6.12`, `3.7.9`, `3.8.5`)
-- Activate them with a command `pyenv shell 3.6.12 3.7.9 3.8.5`
+- Install different versions of python (recommended versions are `3.7.9` and `3.8.5`)
+- Activate them with a command `pyenv shell 3.7.9 3.8.5`
 - Install `tox` by invoking a command `pip install tox`
 - Change a current directory to `${DUCKTESTS_DIR}` and invoke `tox`
diff --git a/modules/ducktests/tests/docker/requirements.txt b/modules/ducktests/tests/docker/requirements.txt
index 06f73eb..bfd0126 100644
--- a/modules/ducktests/tests/docker/requirements.txt
+++ b/modules/ducktests/tests/docker/requirements.txt
@@ -13,4 +13,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-git+https://github.com/Sberbank-Technology/ducktape
+ducktape==0.8.2
diff --git a/modules/ducktests/tests/docker/run_tests.sh b/modules/ducktests/tests/docker/run_tests.sh
index bf20488..20aecee 100755
--- a/modules/ducktests/tests/docker/run_tests.sh
+++ b/modules/ducktests/tests/docker/run_tests.sh
@@ -24,8 +24,8 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 IGNITE_NUM_CONTAINERS=${IGNITE_NUM_CONTAINERS:-13}
 
 # Image name to run nodes
-default_image_name="ducker-ignite-openjdk-8"
-IMAGE_NAME="${IMAGE_NAME:-$default_image_name}"
+JDK_VERSION="${JDK_VERSION:-8}"
+IMAGE_PREFIX="ducker-ignite-openjdk"
 
 ###
 # DuckerTest parameters are specified with options to the script
@@ -82,6 +82,9 @@ The options are as follows:
 -t|--tc-paths
     Path to ducktests. Must be relative path to 'IGNITE/modules/ducktests/tests' directory
 
+--jdk
+    Set jdk version to build, default is 8
+
 EOF
     exit 0
 }
@@ -125,17 +128,15 @@ while [[ $# -ge 1 ]]; do
         -t|--tc-paths) TC_PATHS="$2"; shift 2;;
         -n|--num-nodes) IGNITE_NUM_CONTAINERS="$2"; shift 2;;
         -j|--max-parallel) MAX_PARALLEL="$2"; shift 2;;
+        --jdk) JDK_VERSION="$2"; shift 2;;
         -f|--force) FORCE=$1; shift;;
         *) break;;
     esac
 done
 
-if [[ "$IMAGE_NAME" == "$default_image_name" ]]; then
-    "$SCRIPT_DIR"/ducker-ignite build "$IMAGE_NAME" || die "ducker-ignite build failed"
-else
-    echo "[WARN] Used non-default image $IMAGE_NAME. Be sure you use actual version of the image. " \
-         "Otherwise build it with 'ducker-ignite build' command"
-fi
+
+IMAGE_NAME="$IMAGE_PREFIX-$JDK_VERSION"
+"$SCRIPT_DIR"/ducker-ignite build -j "openjdk:$JDK_VERSION" $IMAGE_NAME || die "ducker-ignite build failed"
 
 if [ -z "$FORCE" ]; then
     # If docker image changed then restart cluster (down here and up within next step)
diff --git a/modules/ducktests/tests/ignitetest/services/ignite.py b/modules/ducktests/tests/ignitetest/services/ignite.py
index 854d9f3..483ef2c 100644
--- a/modules/ducktests/tests/ignitetest/services/ignite.py
+++ b/modules/ducktests/tests/ignitetest/services/ignite.py
@@ -38,7 +38,7 @@ class IgniteService(IgniteAwareService):
         super().__init__(context, config, num_nodes, startup_timeout_sec, shutdown_timeout_sec, modules=modules,
                          jvm_opts=jvm_opts, full_jvm_opts=full_jvm_opts)
 
-    def clean_node(self, node):
+    def clean_node(self, node, **kwargs):
         node.account.kill_java_processes(self.APP_SERVICE_CLASS, clean_shutdown=False, allow_fail=True)
         node.account.ssh("rm -rf -- %s" % self.persistent_root, allow_fail=False)
 
diff --git a/modules/ducktests/tests/ignitetest/services/ignite_app.py b/modules/ducktests/tests/ignitetest/services/ignite_app.py
index 3798feb..277724b 100644
--- a/modules/ducktests/tests/ignitetest/services/ignite_app.py
+++ b/modules/ducktests/tests/ignitetest/services/ignite_app.py
@@ -69,7 +69,7 @@ class IgniteApplicationService(IgniteAwareService):
         except Exception:
             raise Exception("Java application execution failed.") from None
 
-    def clean_node(self, node):
+    def clean_node(self, node, **kwargs):
         if self.alive(node):
             self.logger.warn("%s %s was still alive at cleanup time. Killing forcefully..." %
                              (self.__class__.__name__, node.account))
diff --git a/modules/ducktests/tests/ignitetest/services/spark.py b/modules/ducktests/tests/ignitetest/services/spark.py
index 39ec199..1986331 100644
--- a/modules/ducktests/tests/ignitetest/services/spark.py
+++ b/modules/ducktests/tests/ignitetest/services/spark.py
@@ -21,14 +21,14 @@ import os.path
 from distutils.version import LooseVersion
 
 from ducktape.cluster.remoteaccount import RemoteCommandError
-from ducktape.services.background_thread import BackgroundThreadService
+from ducktape.services.service import Service
 
 from ignitetest.services.utils.path import PathAware
 from ignitetest.services.utils.log_utils import monitor_log
 
 
 # pylint: disable=abstract-method
-class SparkService(BackgroundThreadService, PathAware):
+class SparkService(Service, PathAware):
     """
     Start a spark node.
     """
@@ -56,8 +56,8 @@ class SparkService(BackgroundThreadService, PathAware):
     def globals(self):
         return self.context.globals
 
-    def start(self, clean=True):
-        BackgroundThreadService.start(self, clean=clean)
+    def start(self, **kwargs):
+        super().start(**kwargs)
 
         self.logger.info("Waiting for Spark to start...")
 
@@ -89,7 +89,7 @@ class SparkService(BackgroundThreadService, PathAware):
                 "collect_default": True
             }
 
-    def start_node(self, node):
+    def start_node(self, node, **kwargs):
         self.init_persistent(node)
 
         cmd = self.start_cmd(node)
@@ -113,13 +113,13 @@ class SparkService(BackgroundThreadService, PathAware):
         if len(self.pids(node)) == 0:
             raise Exception("No process ids recorded on node %s" % node.account.hostname)
 
-    def stop_node(self, node):
+    def stop_node(self, node, **kwargs):
         if node == self.nodes[0]:
             node.account.ssh(os.path.join(self.home_dir, "sbin", "stop-master.sh"))
         else:
             node.account.ssh(os.path.join(self.home_dir, "sbin", "stop-slave.sh"))
 
-    def clean_node(self, node):
+    def clean_node(self, node, **kwargs):
         """
         Clean spark persistence files
         """
diff --git a/modules/ducktests/tests/ignitetest/services/utils/background_thread.py b/modules/ducktests/tests/ignitetest/services/utils/background_thread.py
new file mode 100644
index 0000000..ea9e124
--- /dev/null
+++ b/modules/ducktests/tests/ignitetest/services/utils/background_thread.py
@@ -0,0 +1,113 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Background thread service.
+"""
+
+from abc import ABCMeta, abstractmethod
+import threading
+import traceback
+
+from ducktape.services.service import Service
+
+
+class BackgroundThreadService(Service, metaclass=ABCMeta):
+    """BackgroundThreadService allow to start nodes simultaneously using pool of threads."""
+
+    def __init__(self, context, num_nodes=None, cluster_spec=None, **kwargs):
+        super().__init__(context, num_nodes, cluster_spec, **kwargs)
+        self.worker_threads = {}
+        self.worker_errors = {}
+        self.errors = ''
+        self.lock = threading.RLock()
+
+    def _protected_worker(self, idx, node, **kwargs):
+        """Protected worker captures exceptions and makes them available to the main thread.
+
+        This gives us the ability to propagate exceptions thrown in background threads, if desired.
+        """
+        try:
+            self.worker(idx, node, **kwargs)
+        except BaseException:
+            with self.lock:
+                self.logger.info("BackgroundThreadService threw exception: ")
+                trace_fmt = traceback.format_exc()
+                self.logger.info(trace_fmt)
+                self.worker_errors[threading.currentThread().name] = trace_fmt
+                if self.errors:
+                    self.errors += "\n"
+                self.errors += "%s: %s" % (threading.currentThread().name, trace_fmt)
+
+            raise
+
+    @abstractmethod
+    def worker(self, idx, node, **kwargs):
+        """
+        :param idx: Node index
+        :param node: Cluster node instance
+        """
+
+    def start_node(self, node, **kwargs):
+        idx = self.idx(node)
+
+        if idx in self.worker_threads and self.worker_threads[idx].is_alive():
+            raise RuntimeError("Cannot restart node since previous thread is still alive")
+
+        self.logger.info("Running %s node %d on %s", self.service_id, idx, node.account.hostname)
+        worker = threading.Thread(
+            name=self.service_id + "-worker-" + str(idx),
+            target=self._protected_worker,
+            args=(idx, node),
+            kwargs=kwargs
+        )
+        worker.daemon = True
+        worker.start()
+        self.worker_threads[idx] = worker
+
+    def wait(self, timeout_sec=600):
+        """Wait no more than timeout_sec for all worker threads to finish.
+
+        raise TimeoutException if all worker threads do not finish within timeout_sec
+        """
+        super().wait(timeout_sec)
+
+        self._propagate_exceptions()
+
+    def stop(self, **kwargs):
+        alive_workers = sum(1 for worker in self.worker_threads.values() if worker.is_alive())
+        if alive_workers > 0:
+            self.logger.debug(
+                "Called stop with at least one worker thread is still running: " + str(alive_workers))
+
+            self.logger.debug("%s" % str(self.worker_threads))
+
+        super().stop(**kwargs)
+
+        self._propagate_exceptions()
+
+    def wait_node(self, node, timeout_sec=600):
+        idx = self.idx(node)
+        worker_thread = self.worker_threads[idx]
+        worker_thread.join(timeout_sec)
+        return not worker_thread.is_alive()
+
+    def _propagate_exceptions(self):
+        """
+        Propagate exceptions thrown in background threads
+        """
+        with self.lock:
+            if len(self.worker_errors) > 0:
+                raise Exception(self.errors)
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
index 8837bf5..89f7a4f 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
@@ -25,9 +25,9 @@ from abc import abstractmethod, ABCMeta
 from datetime import datetime
 from threading import Thread
 
-from ducktape.services.background_thread import BackgroundThreadService
 from ducktape.utils.util import wait_until
 
+from ignitetest.services.utils.background_thread import BackgroundThreadService
 from ignitetest.services.utils.concurrent import CountDownLatch, AtomicValue
 from ignitetest.services.utils.path import IgnitePathAware
 from ignitetest.services.utils.ignite_spec import resolve_spec
@@ -74,14 +74,14 @@ class IgniteAwareService(BackgroundThreadService, IgnitePathAware, metaclass=ABC
     def globals(self):
         return self.context.globals
 
-    def start_async(self, clean=True):
+    def start_async(self, **kwargs):
         """
         Starts in async way.
         """
-        super().start(clean=clean)
+        super().start(**kwargs)
 
-    def start(self, clean=True):
-        self.start_async(clean=clean)
+    def start(self, **kwargs):
+        self.start_async(**kwargs)
         self.await_started()
 
     def await_started(self):
@@ -92,26 +92,26 @@ class IgniteAwareService(BackgroundThreadService, IgnitePathAware, metaclass=ABC
 
         self.await_event("Topology snapshot", self.startup_timeout_sec, from_the_beginning=True)
 
-    def start_node(self, node):
+    def start_node(self, node, **kwargs):
         self.init_persistent(node)
 
         self.__update_node_log_file(node)
 
-        super().start_node(node)
+        super().start_node(node, **kwargs)
 
         wait_until(lambda: self.alive(node), timeout_sec=10)
 
         ignite_jmx_mixin(node, self.spec, self.pids(node))
 
-    def stop_async(self):
+    def stop_async(self, **kwargs):
         """
         Stop in async way.
         """
-        super().stop()
+        super().stop(**kwargs)
 
-    def stop(self):
+    def stop(self, **kwargs):
         if not self.killed:
-            self.stop_async()
+            self.stop_async(**kwargs)
             self.await_stopped()
         else:
             self.logger.debug("Skipping node stop since it already killed.")
@@ -132,7 +132,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePathAware, metaclass=ABC
                        err_msg="Node %s's remote processes failed to stop in %d seconds" %
                                (str(node.account), self.shutdown_timeout_sec))
 
-    def stop_node(self, node):
+    def stop_node(self, node, **kwargs):
         pids = self.pids(node)
 
         for pid in pids:
@@ -157,10 +157,10 @@ class IgniteAwareService(BackgroundThreadService, IgnitePathAware, metaclass=ABC
 
         self.killed = True
 
-    def clean(self):
+    def clean(self, **kwargs):
         self.__restore_iptables()
 
-        super().clean()
+        super().clean(**kwargs)
 
     def init_persistent(self, node):
         """
@@ -201,7 +201,7 @@ class IgniteAwareService(BackgroundThreadService, IgnitePathAware, metaclass=ABC
         raise NotImplementedError
 
     # pylint: disable=W0613
-    def _worker(self, idx, node):
+    def worker(self, idx, node, **kwargs):
         cmd = self.spec.command(node)
 
         self.logger.debug("Attempting to start Application Service on %s with command: %s" % (str(node.account), cmd))
diff --git a/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py b/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py
index c45fc90..5666f38 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/jmx_utils.py
@@ -139,7 +139,8 @@ class DiscoveryInfo:
         """
         :return: Topology order.
         """
-        return int(self.__find__("order=(\\d+),"))
+        val = self.__find__("order=(\\d+),")
+        return int(val) if val else -1
 
     @property
     def int_order(self):
diff --git a/modules/ducktests/tests/ignitetest/services/utils/jvm_utils.py b/modules/ducktests/tests/ignitetest/services/utils/jvm_utils.py
index 1a4f5a5..2c28ef7 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/jvm_utils.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/jvm_utils.py
@@ -18,23 +18,17 @@ This module contains JVM utilities.
 """
 DEFAULT_HEAP = "768M"
 
-JVM_PARAMS_GC_CMS = "-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSIncrementalMode " \
-                    "-XX:ConcGCThreads=$(((`nproc`/4)>1?(`nproc`/4):1)) " \
-                    "-XX:ParallelGCThreads=$(((`nproc`/2)>1?(`nproc`/2):1)) " \
-                    "-XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly " \
-                    "-XX:+CMSParallelRemarkEnabled -XX:+CMSClassUnloadingEnabled"
-
 JVM_PARAMS_GC_G1 = "-XX:+UseG1GC -XX:MaxGCPauseMillis=100 " \
                    "-XX:ConcGCThreads=$(((`nproc`/3)>1?(`nproc`/3):1)) " \
                    "-XX:ParallelGCThreads=$(((`nproc`*3/4)>1?(`nproc`*3/4):1)) "
 
-JVM_PARAMS_GENERIC = "-server -XX:+DisableExplicitGC -XX:+AggressiveOpts -XX:+AlwaysPreTouch " \
+JVM_PARAMS_GENERIC = "-server -XX:+DisableExplicitGC -XX:+AlwaysPreTouch " \
                      "-XX:+ParallelRefProcEnabled -XX:+DoEscapeAnalysis " \
                      "-XX:+OptimizeStringConcat -XX:+UseStringDeduplication"
 
 
 # pylint: disable=R0913
-def create_jvm_settings(heap_size=DEFAULT_HEAP, gc_settings=JVM_PARAMS_GC_CMS, generic_params=JVM_PARAMS_GENERIC,
+def create_jvm_settings(heap_size=DEFAULT_HEAP, gc_settings=JVM_PARAMS_GC_G1, generic_params=JVM_PARAMS_GENERIC,
                         opts=None, gc_dump_path=None, oom_path=None):
     """
     Provides settings string for JVM process.
@@ -42,8 +36,7 @@ def create_jvm_settings(heap_size=DEFAULT_HEAP, gc_settings=JVM_PARAMS_GC_CMS, g
     """
     gc_dump = ""
     if gc_dump_path:
-        gc_dump = "-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=32M -XX:+PrintGCDateStamps " \
-                  "-verbose:gc -XX:+PrintGCDetails -Xloggc:" + gc_dump_path
+        gc_dump = "-verbose:gc -Xloggc:" + gc_dump_path
 
     out_of_mem_dump = ""
     if oom_path:
diff --git a/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py b/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py
index 76bb479..bd6223b 100644
--- a/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py
+++ b/modules/ducktests/tests/ignitetest/services/zk/zookeeper.py
@@ -81,8 +81,8 @@ class ZookeeperService(Service, PathAware):
     def project(self):
         return "zookeeper"
 
-    def start(self, clean=True):
-        super().start(clean=clean)
+    def start(self, **kwargs):
+        super().start(**kwargs)
         self.logger.info("Waiting for Zookeeper quorum...")
 
         for node in self.nodes:
@@ -90,7 +90,7 @@ class ZookeeperService(Service, PathAware):
 
         self.logger.info("Zookeeper quorum is formed.")
 
-    def start_node(self, node):
+    def start_node(self, node, **kwargs):
         idx = self.idx(node)
 
         self.logger.info("Starting Zookeeper node %d on %s", idx, node.account.hostname)
@@ -163,12 +163,12 @@ class ZookeeperService(Service, PathAware):
         """
         return ','.join([node.account.hostname + ":" + str(2181) for node in self.nodes])
 
-    def stop_node(self, node):
+    def stop_node(self, node, **kwargs):
         idx = self.idx(node)
         self.logger.info("Stopping %s node %d on %s" % (type(self).__name__, idx, node.account.hostname))
         node.account.kill_process("zookeeper", allow_fail=False)
 
-    def clean_node(self, node):
+    def clean_node(self, node, **kwargs):
         self.logger.info("Cleaning Zookeeper node %d on %s", self.idx(node), node.account.hostname)
         if self.alive(node):
             self.logger.warn("%s %s was still alive at cleanup time. Killing forcefully..." %
diff --git a/modules/ducktests/tests/setup.py b/modules/ducktests/tests/setup.py
index 7c658ee..efb5b03 100644
--- a/modules/ducktests/tests/setup.py
+++ b/modules/ducktests/tests/setup.py
@@ -30,8 +30,5 @@ setup(name="ignitetest",
       license="apache2.0",
       packages=find_packages(exclude=["ignitetest.tests", "ignitetest.tests.*"]),
       include_package_data=True,
-      install_requires=["ducktape==0.8.1"],
-      tests_require=["pytest==6.0.1"],
-      dependency_links=[
-          'https://github.com/Sberbank-Technology/ducktape/tarball/master#egg=ducktape-0.8.1'
-      ])
+      install_requires=["ducktape==0.8.2"],
+      tests_require=["pytest==6.0.1"])
diff --git a/modules/ducktests/tests/tox.ini b/modules/ducktests/tests/tox.ini
index 02b773b..d9a0ce4 100644
--- a/modules/ducktests/tests/tox.ini
+++ b/modules/ducktests/tests/tox.ini
@@ -13,14 +13,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 [tox]
-envlist = codestyle, linter, py36, py37, py38
+envlist = codestyle, linter, py37, py38
 skipsdist = True
 
 [travis]
 python =
   3.8: codestyle, linter, py38
   3.7: py37
-  3.6: py36
 
 [testenv]
 envdir = {homedir}/.virtualenvs/ignite-ducktests-{envname}