lennybot 1.0.21__tar.gz → 1.0.26__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.
- {lennybot-1.0.21 → lennybot-1.0.26}/PKG-INFO +1 -1
- {lennybot-1.0.21 → lennybot-1.0.26}/requirements.txt +1 -1
- {lennybot-1.0.21 → lennybot-1.0.26}/setup.py +0 -1
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/actions/iaction.py +3 -3
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/check/docker_image_available.py +28 -9
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/check/icheck.py +4 -4
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/config/config.py +16 -3
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/model/state.py +2 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/github.py +1 -1
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/plan.py +14 -2
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/source/isource.py +2 -2
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot.egg-info/PKG-INFO +1 -1
- {lennybot-1.0.21 → lennybot-1.0.26}/test/test_docker_image_available.py +9 -3
- lennybot-1.0.26/version.txt +1 -0
- lennybot-1.0.21/version.txt +0 -1
- {lennybot-1.0.21 → lennybot-1.0.26}/LICENSE +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/README.md +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/pyproject.toml +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/setup.cfg +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/__main__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/actions/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/actions/download_resources.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/actions/remove_checksums.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/actions/update_dockerfile.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/actions/update_image_tag.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/actions/update_yaml.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/check/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/config/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/helper/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/lennybot.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/model/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/model/plan.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/apply.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/source/__init__.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/source/source_github.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot/service/source/source_github_query.py +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot.egg-info/SOURCES.txt +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot.egg-info/dependency_links.txt +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot.egg-info/entry_points.txt +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot.egg-info/requires.txt +0 -0
- {lennybot-1.0.21 → lennybot-1.0.26}/src/lennybot.egg-info/top_level.txt +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
class IAction:
|
|
2
2
|
@property
|
|
3
|
-
def application(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
3
|
+
def application(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
4
4
|
pass
|
|
5
5
|
|
|
6
6
|
@property
|
|
7
|
-
def source_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
7
|
+
def source_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
8
8
|
pass
|
|
9
9
|
|
|
10
10
|
@property
|
|
11
|
-
def target_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
11
|
+
def target_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
12
12
|
pass
|
|
13
13
|
|
|
14
14
|
def run(self):
|
|
@@ -103,7 +103,11 @@ class DockerImageAvailableCheck(ICheck):
|
|
|
103
103
|
+ image_tag
|
|
104
104
|
)
|
|
105
105
|
return DockerImage(match.group(4), match.group(5) + "/" + match.group(6), image_tag)
|
|
106
|
-
return DockerImage(
|
|
106
|
+
return DockerImage(
|
|
107
|
+
match.group(7),
|
|
108
|
+
match.group(8) + "/" + match.group(9) + "/" + match.group(10),
|
|
109
|
+
image_tag,
|
|
110
|
+
)
|
|
107
111
|
|
|
108
112
|
def _authenticate_on_registry(self, registry: str, authentication_header: WwwAuthenticateHeader) -> str:
|
|
109
113
|
params = {
|
|
@@ -118,8 +122,12 @@ class DockerImageAvailableCheck(ICheck):
|
|
|
118
122
|
|
|
119
123
|
if registry in self._container_config.registries.keys():
|
|
120
124
|
registry_data = self._container_config.registries[registry]
|
|
121
|
-
password = registry_data.password
|
|
122
125
|
username = registry_data.username
|
|
126
|
+
password = registry_data.password
|
|
127
|
+
if "<REDACTED>" in [username, password]:
|
|
128
|
+
logging.warning(
|
|
129
|
+
"Either username or password contain '<REDACTED>' and probably have not been overwritten"
|
|
130
|
+
)
|
|
123
131
|
response = requests.get(url, auth=(username, password))
|
|
124
132
|
else:
|
|
125
133
|
logging.debug("Registry not found in config")
|
|
@@ -139,15 +147,23 @@ class DockerImageAvailableCheck(ICheck):
|
|
|
139
147
|
return str(access_token)
|
|
140
148
|
|
|
141
149
|
if response.status_code == 401:
|
|
142
|
-
logging.error(
|
|
150
|
+
logging.error(
|
|
151
|
+
"Authentication failed: %d with %s",
|
|
152
|
+
response.status_code,
|
|
153
|
+
response.headers,
|
|
154
|
+
)
|
|
143
155
|
raise Exception("Error occurred: Unauthenticated: ", response.status_code)
|
|
144
156
|
|
|
145
157
|
if response.status_code == 403:
|
|
146
|
-
logging.error(
|
|
158
|
+
logging.error(
|
|
159
|
+
"Authorization failed: %d with %s",
|
|
160
|
+
response.status_code,
|
|
161
|
+
response.headers,
|
|
162
|
+
)
|
|
147
163
|
raise Exception("Error occurred: Unauthorization: ", response.status_code)
|
|
148
164
|
|
|
149
165
|
if response.status_code == 404:
|
|
150
|
-
logging.error("Nothing Found:", response.status_code, response.headers)
|
|
166
|
+
logging.error("Nothing Found: %d with %s", response.status_code, response.headers)
|
|
151
167
|
raise Exception("Error occurred: Nothing Found: ", response.status_code)
|
|
152
168
|
|
|
153
169
|
raise Exception("Unexpected Status Code", response.status_code)
|
|
@@ -176,11 +192,14 @@ class DockerImageAvailableCheck(ICheck):
|
|
|
176
192
|
|
|
177
193
|
request_url = f"https://{image._registry}/v2/{image._name}/manifests/{image._tag}"
|
|
178
194
|
|
|
195
|
+
# depending on the registry it my helps adding the write accept header :)
|
|
196
|
+
# https://github.com/goharbor/harbor/issues/16075
|
|
197
|
+
headers = {
|
|
198
|
+
"Accept": "application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json",
|
|
199
|
+
}
|
|
179
200
|
if access_token is not None:
|
|
180
|
-
headers
|
|
181
|
-
|
|
182
|
-
else:
|
|
183
|
-
response = requests.get(request_url)
|
|
201
|
+
headers["Authorization"] = f"Bearer {access_token}"
|
|
202
|
+
response = requests.get(request_url, headers=headers)
|
|
184
203
|
|
|
185
204
|
if response.status_code == 401 and access_token is None:
|
|
186
205
|
registry = image._registry
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
class ICheck:
|
|
2
2
|
@property
|
|
3
|
-
def application(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
3
|
+
def application(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
4
4
|
pass
|
|
5
5
|
|
|
6
6
|
@property
|
|
7
|
-
def source_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
7
|
+
def source_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
8
8
|
pass
|
|
9
9
|
|
|
10
10
|
@property
|
|
11
|
-
def target_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
11
|
+
def target_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
12
12
|
pass
|
|
13
13
|
|
|
14
|
-
def check(self) -> bool: # pyright: ignore [reportGeneralTypeIssues]
|
|
14
|
+
def check(self) -> bool: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
15
15
|
pass
|
|
@@ -26,7 +26,13 @@ CONFIGURATION_OPTIONS = {
|
|
|
26
26
|
},
|
|
27
27
|
"logging": {
|
|
28
28
|
"type": "object",
|
|
29
|
-
"properties": {
|
|
29
|
+
"properties": {
|
|
30
|
+
"level": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"required": False,
|
|
33
|
+
"attribute": "_logging_level",
|
|
34
|
+
}
|
|
35
|
+
},
|
|
30
36
|
},
|
|
31
37
|
"container": {
|
|
32
38
|
"type": "object",
|
|
@@ -56,7 +62,11 @@ CONFIGURATION_OPTIONS = {
|
|
|
56
62
|
"attribute": "_source",
|
|
57
63
|
"properties": {
|
|
58
64
|
"type": {"type": "string", "required": True, "attribute": "_type"},
|
|
59
|
-
"repository": {
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"required": True,
|
|
68
|
+
"attribute": "_repository",
|
|
69
|
+
},
|
|
60
70
|
"regex": {"type": "string", "attribute": "_regex"},
|
|
61
71
|
},
|
|
62
72
|
},
|
|
@@ -259,7 +269,10 @@ class LennyBotConfig:
|
|
|
259
269
|
|
|
260
270
|
def _configure_logging(self):
|
|
261
271
|
logging_level = logging._nameToLevel.get(self._logging_level, logging.DEBUG)
|
|
262
|
-
logging.basicConfig(
|
|
272
|
+
logging.basicConfig(
|
|
273
|
+
level=logging_level,
|
|
274
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
275
|
+
)
|
|
263
276
|
self._log = logging.getLogger(self.__class__.__name__)
|
|
264
277
|
self._log.debug("Logging was configured")
|
|
265
278
|
|
|
@@ -13,6 +13,8 @@ class LennyBotState:
|
|
|
13
13
|
self._hash = self._calculate_hash()
|
|
14
14
|
with open(self._filename, encoding="utf-8") as file_ptr:
|
|
15
15
|
self._data = yaml.safe_load(file_ptr)
|
|
16
|
+
if self._data is None:
|
|
17
|
+
self._data = {}
|
|
16
18
|
|
|
17
19
|
def _init_file(self):
|
|
18
20
|
if not os.path.exists(self._filename):
|
|
@@ -34,7 +34,7 @@ class GitHubService:
|
|
|
34
34
|
if self._github is None:
|
|
35
35
|
raise Exception("GitHub is not configured")
|
|
36
36
|
repo = self._github.get_repo(self._config.github_pr.repository)
|
|
37
|
-
new_pull = repo.create_pull(
|
|
37
|
+
new_pull = repo.create_pull(repo.default_branch, branch_name, title=title, body=body)
|
|
38
38
|
labels = self._get_or_create_labels(repo)
|
|
39
39
|
new_pull.add_to_labels(*labels)
|
|
40
40
|
pulls = self._find_own_pulls()
|
|
@@ -31,7 +31,13 @@ class LennyBotApplication:
|
|
|
31
31
|
self._current_version = state.current_version(self._name)
|
|
32
32
|
self._latest_version = self._source.latest_version()
|
|
33
33
|
for config in self._config._checks:
|
|
34
|
-
check = create_check(
|
|
34
|
+
check = create_check(
|
|
35
|
+
self.name,
|
|
36
|
+
self._current_version,
|
|
37
|
+
self._latest_version,
|
|
38
|
+
config,
|
|
39
|
+
self._global_config,
|
|
40
|
+
)
|
|
35
41
|
self._checks.append(check)
|
|
36
42
|
|
|
37
43
|
def should_update(self) -> bool:
|
|
@@ -47,7 +53,13 @@ class LennyBotApplication:
|
|
|
47
53
|
|
|
48
54
|
for check in self._checks:
|
|
49
55
|
if not check.check():
|
|
50
|
-
self._log.
|
|
56
|
+
self._log.warning(
|
|
57
|
+
"Check '%s' failed for application '%s' with current version '%s' and latest version '%s'",
|
|
58
|
+
check.__class__.__name__,
|
|
59
|
+
self.name,
|
|
60
|
+
self._current_version,
|
|
61
|
+
self._latest_version,
|
|
62
|
+
)
|
|
51
63
|
return False
|
|
52
64
|
return True
|
|
53
65
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
class ISource:
|
|
2
2
|
@property
|
|
3
|
-
def application(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
3
|
+
def application(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
4
4
|
pass
|
|
5
5
|
|
|
6
|
-
def latest_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues]
|
|
6
|
+
def latest_version(self) -> str: # pyright: ignore [reportGeneralTypeIssues, reportReturnType]
|
|
7
7
|
pass
|
|
@@ -54,7 +54,9 @@ class TestAuthenticateImage(unittest.TestCase):
|
|
|
54
54
|
def test_authenticate_on_registry_returns_access_token(self):
|
|
55
55
|
registry = LennyBotConfigContainerRegistry("hub.docker.io")
|
|
56
56
|
header_value = WwwAuthenticateHeader(
|
|
57
|
-
"https://docker-auth.elastic.co/auth",
|
|
57
|
+
"https://docker-auth.elastic.co/auth",
|
|
58
|
+
"repository:beats/filebeat:pull",
|
|
59
|
+
"token-service",
|
|
58
60
|
)
|
|
59
61
|
access_token = self.docker_image_check._authenticate_on_registry(registry.name, header_value)
|
|
60
62
|
self.assertIsNotNone(access_token)
|
|
@@ -62,7 +64,9 @@ class TestAuthenticateImage(unittest.TestCase):
|
|
|
62
64
|
def test_authenticate_without_credentials_in_config(self):
|
|
63
65
|
self.config._image_pattern = "quay.io/argoproj/argocd:v{{version}}"
|
|
64
66
|
header_value = WwwAuthenticateHeader(
|
|
65
|
-
"https://quay.io/v2/auth",
|
|
67
|
+
"https://quay.io/v2/auth",
|
|
68
|
+
"repository:argoproj/argocd:pull",
|
|
69
|
+
"token-service",
|
|
66
70
|
)
|
|
67
71
|
access_token = self.docker_image_check._authenticate_on_registry("", header_value)
|
|
68
72
|
self.assertIsNotNone(access_token)
|
|
@@ -74,7 +78,9 @@ class TestAuthenticateImage(unittest.TestCase):
|
|
|
74
78
|
registry._password = "1234"
|
|
75
79
|
self.container_config._registries["ghcr.io"] = registry
|
|
76
80
|
header_value = WwwAuthenticateHeader(
|
|
77
|
-
"https://ghcr.io/v2/auth",
|
|
81
|
+
"https://ghcr.io/v2/auth",
|
|
82
|
+
"repository:brose-ebike/postgres-operator:pull",
|
|
83
|
+
"token-service",
|
|
78
84
|
)
|
|
79
85
|
|
|
80
86
|
with self.assertRaises(Exception) as context:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.26
|
lennybot-1.0.21/version.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.0.21
|
|
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
|
|
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
|