dfsync 0.4.7__tar.gz → 0.4.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dfsync-0.4.7 → dfsync-0.4.8}/PKG-INFO +2 -2
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/distribution.py +50 -8
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/kube_credentials.py +3 -1
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/monitor.py +37 -6
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/test_dfsync.py +0 -4
- dfsync-0.4.8/dfsync/test_distribution.py +30 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/test_kube_credentials.py +17 -4
- {dfsync-0.4.7 → dfsync-0.4.8}/pyproject.toml +3 -2
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/__init__.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/backends/__init__.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/backends/kube.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/backends/kube_exec.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/backends/rsync.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/char_ui.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/chcli.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/check_dns.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/cli.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/config.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/diffui.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/exp.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/filters.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/lib.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/test_lib.py +0 -0
- {dfsync-0.4.7 → dfsync-0.4.8}/dfsync/transactions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dfsync
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Mihai Balint
|
|
6
6
|
Author-email: balint.mihai@gmail.com
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Requires-Dist: black (==
|
|
14
|
+
Requires-Dist: black (==24.3.0)
|
|
15
15
|
Requires-Dist: click (>=8,<9)
|
|
16
16
|
Requires-Dist: click-default-group (>=1.2.2,<2.0.0)
|
|
17
17
|
Requires-Dist: gitpython (>=3.1.14,<4.0.0)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import click
|
|
1
2
|
import json
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
2
5
|
import urllib.request
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
3
8
|
from dfsync.lib import ControlledThreadedOperation
|
|
4
9
|
|
|
5
10
|
|
|
@@ -8,14 +13,14 @@ def get_package_version(package_name):
|
|
|
8
13
|
from importlib.metadata import version
|
|
9
14
|
|
|
10
15
|
return version(package_name)
|
|
11
|
-
except:
|
|
16
|
+
except Exception:
|
|
12
17
|
pass
|
|
13
18
|
|
|
14
19
|
try:
|
|
15
20
|
import pkg_resources
|
|
16
21
|
|
|
17
22
|
return pkg_resources.get_distribution(package_name).version
|
|
18
|
-
except:
|
|
23
|
+
except Exception:
|
|
19
24
|
pass
|
|
20
25
|
|
|
21
26
|
return None
|
|
@@ -41,7 +46,7 @@ def get_latest_version():
|
|
|
41
46
|
raise ValueError("No version visible on github")
|
|
42
47
|
|
|
43
48
|
return versions[0]
|
|
44
|
-
except Exception
|
|
49
|
+
except Exception:
|
|
45
50
|
return get_installed_version()
|
|
46
51
|
|
|
47
52
|
|
|
@@ -52,7 +57,7 @@ def parse_version(version_str):
|
|
|
52
57
|
for part in version_str.split("."):
|
|
53
58
|
try:
|
|
54
59
|
parsed_version.append(int(part))
|
|
55
|
-
except:
|
|
60
|
+
except Exception:
|
|
56
61
|
parsed_version.append(part)
|
|
57
62
|
return parsed_version
|
|
58
63
|
|
|
@@ -80,15 +85,52 @@ class AsyncVersionChecker(ControlledThreadedOperation):
|
|
|
80
85
|
self.installed = get_installed_version()
|
|
81
86
|
self.latest = get_latest_version()
|
|
82
87
|
self.installed_is_older = is_older_version(self.installed, self.latest)
|
|
88
|
+
self._is_install_editable = is_installed_in_editable_mode("dfsync")
|
|
83
89
|
self._cta_count = 0
|
|
84
90
|
self.stop()
|
|
85
91
|
|
|
86
92
|
@property
|
|
87
|
-
def
|
|
93
|
+
def should_emit_update_warning(self):
|
|
88
94
|
return self.is_completed and self.installed_is_older and self._cta_count < 1
|
|
89
95
|
|
|
90
|
-
def
|
|
96
|
+
def set_emitted_update_warning(self):
|
|
91
97
|
self._cta_count += 1
|
|
92
98
|
|
|
93
|
-
def
|
|
94
|
-
|
|
99
|
+
def get_update_warning(self):
|
|
100
|
+
if self._is_install_editable:
|
|
101
|
+
return f"dfsync ver. {self.latest} is available, please update! ({self.installed} is installed)"
|
|
102
|
+
else:
|
|
103
|
+
return f"dfsync ver. {self.latest} is available ({self.installed} is installed), please update using `dfsync self-update`"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _read_editable_location_from_pip_show_output(out: str) -> Optional[str]:
|
|
107
|
+
editable_label = "Editable project location: "
|
|
108
|
+
site_packages_label = "site-packages"
|
|
109
|
+
site_packages_lines = []
|
|
110
|
+
|
|
111
|
+
for line in out.split("\n"):
|
|
112
|
+
if line.startswith("Location: "):
|
|
113
|
+
continue
|
|
114
|
+
if line.startswith(editable_label):
|
|
115
|
+
return line[len(editable_label) :].strip()
|
|
116
|
+
if site_packages_label in line:
|
|
117
|
+
site_packages_lines.append(line)
|
|
118
|
+
|
|
119
|
+
return "UNKNOWN-EDITABLE-LOCATION" if len(site_packages_lines) > 0 else None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def is_installed_in_editable_mode(package_name: str) -> bool:
|
|
123
|
+
try:
|
|
124
|
+
out = subprocess.check_output([sys.executable, "-m", "pip", "show", "-f", package_name]).decode()
|
|
125
|
+
editable_location = _read_editable_location_from_pip_show_output(out)
|
|
126
|
+
return editable_location is not None
|
|
127
|
+
except Exception:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def update_package(package_name: str):
|
|
132
|
+
click.echo(f"Running {sys.executable} -m pip install --upgrade {package_name}")
|
|
133
|
+
try:
|
|
134
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", package_name])
|
|
135
|
+
except Exception as e:
|
|
136
|
+
click.echo(f"Failed to update {package_name}: {e}")
|
|
@@ -77,14 +77,16 @@ def update_local_kube_config(new_kube_credentials):
|
|
|
77
77
|
credentials_yaml = yaml.dump(credentials)
|
|
78
78
|
with open(f"{LOCAL_CREDENTIALS_FILE}.new", "w") as f:
|
|
79
79
|
f.write(credentials_yaml)
|
|
80
|
+
os.chmod(f"{LOCAL_CREDENTIALS_FILE}.new", 0o600)
|
|
80
81
|
|
|
81
82
|
if os.path.isfile(LOCAL_CREDENTIALS_FILE):
|
|
82
83
|
if os.path.isfile(f"{LOCAL_CREDENTIALS_FILE}.old"):
|
|
83
84
|
os.remove(f"{LOCAL_CREDENTIALS_FILE}.old")
|
|
84
85
|
os.rename(f"{LOCAL_CREDENTIALS_FILE}", f"{LOCAL_CREDENTIALS_FILE}.old")
|
|
86
|
+
os.chmod(f"{LOCAL_CREDENTIALS_FILE}.old", 0o600)
|
|
85
87
|
|
|
86
88
|
os.rename(f"{LOCAL_CREDENTIALS_FILE}.new", f"{LOCAL_CREDENTIALS_FILE}")
|
|
87
|
-
|
|
89
|
+
os.chmod(LOCAL_CREDENTIALS_FILE, 0o600)
|
|
88
90
|
return credentials_yaml
|
|
89
91
|
|
|
90
92
|
|
|
@@ -13,7 +13,14 @@ from watchdog.observers import Observer
|
|
|
13
13
|
from watchdog.events import FileSystemEventHandler
|
|
14
14
|
|
|
15
15
|
from dfsync.backends import rsync_backend, kube_backend
|
|
16
|
-
from dfsync.distribution import
|
|
16
|
+
from dfsync.distribution import (
|
|
17
|
+
get_installed_version,
|
|
18
|
+
get_latest_version,
|
|
19
|
+
is_older_version,
|
|
20
|
+
AsyncVersionChecker,
|
|
21
|
+
is_installed_in_editable_mode,
|
|
22
|
+
update_package,
|
|
23
|
+
)
|
|
17
24
|
from dfsync.filters import add_user_ignored_patterns_filter, ALL_FILTERS
|
|
18
25
|
from dfsync.config import read_config
|
|
19
26
|
from dfsync.char_ui import KeyController
|
|
@@ -253,12 +260,36 @@ def main():
|
|
|
253
260
|
|
|
254
261
|
@main.command()
|
|
255
262
|
def version():
|
|
263
|
+
"""
|
|
264
|
+
Print the currently installed version
|
|
265
|
+
"""
|
|
256
266
|
installed_version = get_installed_version()
|
|
257
267
|
click.echo(f"{installed_version}")
|
|
258
268
|
|
|
259
269
|
latest_version = get_latest_version()
|
|
260
|
-
if is_older_version(installed_version, latest_version):
|
|
261
|
-
|
|
270
|
+
if not is_older_version(installed_version, latest_version):
|
|
271
|
+
return
|
|
272
|
+
elif is_installed_in_editable_mode("dfsync"):
|
|
273
|
+
click.echo(f"Latest version is {latest_version}, please update!")
|
|
274
|
+
else:
|
|
275
|
+
click.echo(f"Latest version is {latest_version}, please update using `dfsync self-update`")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@main.command()
|
|
279
|
+
def self_update():
|
|
280
|
+
"""
|
|
281
|
+
Update dfsync to the latest version published on pypi
|
|
282
|
+
"""
|
|
283
|
+
latest_version = get_latest_version()
|
|
284
|
+
installed_version = get_installed_version()
|
|
285
|
+
if not is_older_version(installed_version, latest_version):
|
|
286
|
+
click.echo(f"The latest version is already installed: {installed_version}")
|
|
287
|
+
return
|
|
288
|
+
elif is_installed_in_editable_mode("dfsync"):
|
|
289
|
+
click.echo("dfsync is installed in editable mode, please update manually")
|
|
290
|
+
return
|
|
291
|
+
else:
|
|
292
|
+
update_package("dfsync")
|
|
262
293
|
|
|
263
294
|
|
|
264
295
|
@main.command()
|
|
@@ -419,9 +450,9 @@ def sync(source, destination, supervisor, kube_host, pod_timeout, full_sync):
|
|
|
419
450
|
no_errors = False
|
|
420
451
|
break
|
|
421
452
|
with controller.getch_lock():
|
|
422
|
-
if checker.
|
|
423
|
-
checker.
|
|
424
|
-
click.echo(checker.
|
|
453
|
+
if checker.should_emit_update_warning:
|
|
454
|
+
checker.set_emitted_update_warning()
|
|
455
|
+
click.echo(checker.get_update_warning())
|
|
425
456
|
controller.raise_exceptions()
|
|
426
457
|
|
|
427
458
|
except KeyboardInterrupt:
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from dfsync.distribution import is_installed_in_editable_mode, get_package_version, update_package
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_is_installed_in_editable_mode():
|
|
6
|
+
assert is_installed_in_editable_mode("dfsync") is True
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_is_installed_in_editable_mode_missing_package():
|
|
10
|
+
assert is_installed_in_editable_mode("does-not-exist") is False
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_is_installed_in_editable_mode_required_and_installed_package():
|
|
14
|
+
assert pytest is not None, "pytest must be installed"
|
|
15
|
+
assert is_installed_in_editable_mode("pytest") is False
|
|
16
|
+
|
|
17
|
+
assert is_installed_in_editable_mode("click") is False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_update_package():
|
|
21
|
+
tqdm_version = get_package_version("tqdm")
|
|
22
|
+
|
|
23
|
+
update_package("tqdm==4.66.2")
|
|
24
|
+
assert get_package_version("tqdm") == "4.66.2"
|
|
25
|
+
|
|
26
|
+
update_package("tqdm==4.66.0")
|
|
27
|
+
assert get_package_version("tqdm") == "4.66.0"
|
|
28
|
+
|
|
29
|
+
update_package(f"tqdm=={tqdm_version}")
|
|
30
|
+
assert get_package_version("tqdm") == tqdm_version
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import pytest
|
|
2
|
-
import yaml
|
|
3
3
|
import tempfile
|
|
4
|
+
import yaml
|
|
4
5
|
|
|
5
6
|
from dfsync.kube_credentials import update_local_kube_config, normalized_k8s_url
|
|
6
7
|
|
|
@@ -12,7 +13,7 @@ def mock_kube_credentials(mocker):
|
|
|
12
13
|
return temp.name
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
def
|
|
16
|
+
def test_update_missing_kube_config_and_check_permissions(mock_kube_credentials):
|
|
16
17
|
new_kube_credentials = {
|
|
17
18
|
"clusters": [{"cluster": {"server": "test_server"}, "name": "test_cluster"}],
|
|
18
19
|
"contexts": [{"context": {"cluster": "test_cluster", "user": "test_user"}, "name": "test_context"}],
|
|
@@ -23,6 +24,9 @@ def test_update_missing_kube_config(mock_kube_credentials):
|
|
|
23
24
|
assert "test_context" in result
|
|
24
25
|
assert "test_user" in result
|
|
25
26
|
|
|
27
|
+
# Check file permissions
|
|
28
|
+
assert oct(os.stat(mock_kube_credentials).st_mode)[-3:] == "600"
|
|
29
|
+
|
|
26
30
|
|
|
27
31
|
def test_update_empty_kube_config(mock_kube_credentials):
|
|
28
32
|
with open(mock_kube_credentials, "wb") as f:
|
|
@@ -53,6 +57,15 @@ def test_update_empty_kube_config(mock_kube_credentials):
|
|
|
53
57
|
assert credentials["users"][0]["user"]["username"] == "test_username"
|
|
54
58
|
assert credentials["users"][0]["user"]["password"] == "test_password"
|
|
55
59
|
|
|
60
|
+
# Verify that the old file was saved
|
|
61
|
+
with open(f"{mock_kube_credentials}.old", "r") as f:
|
|
62
|
+
credentials = yaml.safe_load(f)
|
|
63
|
+
assert credentials == empty_config
|
|
64
|
+
|
|
65
|
+
# Check file permissions for both old and new config file
|
|
66
|
+
assert oct(os.stat(mock_kube_credentials).st_mode)[-3:] == "600"
|
|
67
|
+
assert oct(os.stat(f"{mock_kube_credentials}.old").st_mode)[-3:] == "600"
|
|
68
|
+
|
|
56
69
|
|
|
57
70
|
def test_update_existing_kube_config(mock_kube_credentials):
|
|
58
71
|
with open(mock_kube_credentials, "wb") as f:
|
|
@@ -103,5 +116,5 @@ def test_normalized_k8s_url():
|
|
|
103
116
|
assert normalized_k8s_url("192.168.0.123:6443") == "https://192.168.0.123:6443"
|
|
104
117
|
with pytest.raises(ValueError):
|
|
105
118
|
normalized_k8s_url("")
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
|
|
120
|
+
assert normalized_k8s_url(None) is None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dfsync"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.8"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = ["Mihai Balint <balint.mihai@gmail.com>"]
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ toml = "^0.10.2"
|
|
|
14
14
|
tenacity = "^8.0.1"
|
|
15
15
|
click-default-group = "^1.2.2"
|
|
16
16
|
paramiko = {version = "^3.2.0", extras = ["invoke"]}
|
|
17
|
-
black = "
|
|
17
|
+
black = "24.3.0"
|
|
18
18
|
|
|
19
19
|
[tool.poetry.dev-dependencies]
|
|
20
20
|
pytest = "^7.4"
|
|
@@ -23,6 +23,7 @@ ipython = "^8"
|
|
|
23
23
|
ipdb = "^0.13"
|
|
24
24
|
flake8 = "^4.0.1"
|
|
25
25
|
pycodestyle = "^2.8.0"
|
|
26
|
+
tqdm = "4.66.0"
|
|
26
27
|
|
|
27
28
|
[tool.black]
|
|
28
29
|
line-length = 120
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|