Skip to content

Commit

Permalink
ccmlib: Remove usages of distutils
Browse files Browse the repository at this point in the history
This change removes all uses of distutils modules and replaces them with
either direct replacements or extractions from the library.
LooseVersion is extracted into ccmlib.utils.version and dir_util.copy_tree
is replaced by shutil.copytree.

Fixes scylladb#537
  • Loading branch information
k0machi committed Dec 19, 2023
1 parent 40f3517 commit dd6ea47
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 15 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Windows only:
Installation
------------

ccm uses python distutils so from the source directory run:
ccm uses python setuptools (with distutils fallback) so from the source directory run:

sudo ./setup.py install

Expand Down
11 changes: 6 additions & 5 deletions ccmlib/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
import tarfile
import tempfile
import time
from typing import List
import urllib
from distutils.version import LooseVersion


from ccmlib.utils.version import LooseVersion as Version
from ccmlib.common import (ArgumentError, CCMError, get_default_path,
platform_binary, rmdirs, validate_install_dir,
assert_jdk_valid_for_cassandra_version, get_version_from_build)
Expand Down Expand Up @@ -373,7 +374,7 @@ def get_tagged_version_numbers(series='stable'):
"""Retrieve git tags and find version numbers for a release series
series - 'stable', 'oldstable', or 'testing'"""
releases = []
releases: List[Version] = []
if series == 'testing':
# Testing releases always have a hyphen after the version number:
tag_regex = re.compile(r'^refs/tags/cassandra-([0-9]+\.[0-9]+\.[0-9]+-.*$)')
Expand All @@ -385,15 +386,15 @@ def get_tagged_version_numbers(series='stable'):
for ref in (i.get('ref', '') for i in json.loads(tag_url.read())):
m = tag_regex.match(ref)
if m:
releases.append(LooseVersion(m.groups()[0]))
releases.append(Version(m.groups()[0]))

# Sort by semver:
releases.sort(reverse=True)

stable_major_version = LooseVersion(str(releases[0].version[0]) + "." + str(releases[0].version[1]))
stable_major_version = Version(str(releases[0].version[0]) + "." + str(releases[0].version[1]))
stable_releases = [r for r in releases if r >= stable_major_version]
oldstable_releases = [r for r in releases if r not in stable_releases]
oldstable_major_version = LooseVersion(str(oldstable_releases[0].version[0]) + "." + str(oldstable_releases[0].version[1]))
oldstable_major_version = Version(str(oldstable_releases[0].version[0]) + "." + str(oldstable_releases[0].version[1]))
oldstable_releases = [r for r in oldstable_releases if r >= oldstable_major_version]

if series == 'testing':
Expand Down
10 changes: 5 additions & 5 deletions ccmlib/scylla_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
import uuid
import datetime

from distutils.version import LooseVersion

from ccmlib import common
from ccmlib.cluster import Cluster
from ccmlib.scylla_node import ScyllaNode
from ccmlib.node import NodeError
from ccmlib import scylla_repository
from ccmlib.utils.sni_proxy import stop_sni_proxy
from ccmlib.utils.version import LooseVersion as Version

SNITCH = 'org.apache.cassandra.locator.GossipingPropertyFileSnitch'

Expand Down Expand Up @@ -294,7 +294,7 @@ def __init__(self, scylla_cluster, install_dir=None):
def version(self):
stdout, _ = self.sctool(["version"], ignore_exit_status=True)
version_string = stdout[stdout.find(": ") + 2:].strip() # Removing unnecessary information
version_code = LooseVersion(version_string)
version_code = Version(version_string)
return version_code

def _install(self, install_dir):
Expand All @@ -314,8 +314,8 @@ def _update_config(self, install_dir=None):
data['database'] = {}
data['database']['hosts'] = [self.scylla_cluster.get_node_ip(1)]
data['database']['replication_factor'] = 3
if install_dir and (self.version < LooseVersion("2.5") or
LooseVersion('666') < self.version < LooseVersion('666.dev-0.20210430.2217cc84')):
if install_dir and (self.version < Version("2.5") or
Version('666') < self.version < Version('666.dev-0.20210430.2217cc84')):
data['database']['migrate_dir'] = os.path.join(install_dir, 'schema', 'cql')
if 'https' in data:
del data['https']
Expand All @@ -328,7 +328,7 @@ def _update_config(self, install_dir=None):
data['logger']['mode'] = 'stderr'
if not 'repair' in data:
data['repair'] = {}
if self.version < LooseVersion("2.2"):
if self.version < Version("2.2"):
data['repair']['segments_per_repair'] = 16
data['prometheus'] = f"{self.scylla_cluster.get_node_ip(1)}:56091"
# Changing port to 56091 since the manager and the first node share the same ip and 56090 is already in use
Expand Down
4 changes: 2 additions & 2 deletions ccmlib/utils/sni_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from contextlib import contextmanager
import tempfile
from textwrap import dedent
import distutils.dir_util
from shutil import copytree
from dataclasses import dataclass

import yaml
Expand Down Expand Up @@ -198,7 +198,7 @@ def refresh_certs(cluster, nodes_info):
dns_names = ['cql.cluster-id.scylla.com'] + \
[f'{node.host_id}.cql.cluster-id.scylla.com' for node in nodes_info]
generate_ssl_stores(tmp_dir, dns_names=dns_names)
distutils.dir_util.copy_tree(tmp_dir, cluster.get_path())
copytree(tmp_dir, cluster.get_path(), dirs_exist_ok=True)


if __name__ == "__main__":
Expand Down
69 changes: 69 additions & 0 deletions ccmlib/utils/version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,74 @@
import re
from packaging.version import parse, Version

class LooseVersion:
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)

def __init__ (self, vstring=None):
if vstring:
self.parse(vstring)

def parse (self, vstring):
self.vstring = vstring
components = [x for x in self.component_re.split(vstring)
if x and x != '.']
for i, obj in enumerate(components):
try:
components[i] = int(obj)
except ValueError:
pass

self.version = components

def _cmp (self, other):
if isinstance(other, str):
other = LooseVersion(other)
elif not isinstance(other, LooseVersion):
return NotImplemented

if self.version == other.version:
return 0
if self.version < other.version:
return -1
if self.version > other.version:
return 1

def __str__ (self):
return self.vstring

def __repr__ (self):
return "LooseVersion ('%s')" % str(self)

def __eq__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c == 0

def __lt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c < 0

def __le__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c <= 0

def __gt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c > 0

def __ge__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c >= 0


def parse_version(v: str) -> Version:
v = v.replace('~', '-')
Expand Down
14 changes: 12 additions & 2 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import distutils.util
import os

DEV_MODE = bool(distutils.util.strtobool(os.environ.get("DEV_MODE", "False")))

def strtobool(val: str):
normalized_val = val.lower()
if normalized_val in ('y', 'yes', 't', 'true', 'on', '1'):
return True
if normalized_val in ('n', 'no', 'f', 'false', 'off', '0'):
return False
else:
raise ValueError(f"invalid truth value {val}")


DEV_MODE = strtobool(os.environ.get("DEV_MODE", "False"))
RESULTS_DIR = "test_results"
TEST_ID = os.environ.get("CCM_TEST_ID", None)
SCYLLA_DOCKER_IMAGE = os.environ.get(
Expand Down

0 comments on commit dd6ea47

Please sign in to comment.