kubernetes-watch 0.1.10__py3-none-any.whl → 0.1.12__py3-none-any.whl

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.
@@ -1,7 +1,6 @@
1
1
  from prefect import get_run_logger
2
2
  from typing import List
3
- from kubernetes import config
4
- from kubernetes import client
3
+ from kubernetes import config, client, utils
5
4
  from kubernetes.client.rest import ApiException
6
5
  import base64
7
6
  import datetime
@@ -183,4 +182,76 @@ def has_mismatch_image_digest(repo_digest, label_selector, namespace):
183
182
  logger.info("Images are in-sync.")
184
183
  logger.info(f"Repo digest: {repo_digest}")
185
184
  logger.info(f"Curr digest: {current_image_id.split('@')[-1]}")
186
- return False
185
+ return False
186
+
187
+
188
+ def update_deployment_image_if_needed(namespace, deployment_name, container_name, new_tag):
189
+ """
190
+ Updates the deployment's container image tag only if it differs from the current one.
191
+ The repository URI is extracted automatically from the current image reference.
192
+
193
+ Args:
194
+ namespace: Kubernetes namespace where the deployment resides.
195
+ deployment_name: Name of the deployment to patch.
196
+ container_name: Name of the container inside the deployment.
197
+ new_tag: The new image tag to set, e.g. 'v1.2.3'
198
+
199
+ Returns:
200
+ True if the image was updated and rollout triggered, False otherwise.
201
+ """
202
+ try:
203
+ # Load kube config (works both in-cluster and local)
204
+ try:
205
+ config.load_incluster_config()
206
+ except config.ConfigException:
207
+ config.load_kube_config()
208
+
209
+ apps_v1 = client.AppsV1Api()
210
+
211
+ # Read the current deployment
212
+ deployment = apps_v1.read_namespaced_deployment(deployment_name, namespace)
213
+
214
+ # Find the container we want to update
215
+ containers = deployment.spec.template.spec.containers
216
+ target_container = next((c for c in containers if c.name == container_name), None)
217
+ if not target_container:
218
+ logger.error(f"Container '{container_name}' not found in deployment '{deployment_name}'.")
219
+ return False
220
+
221
+ current_image = target_container.image
222
+ if ':' in current_image:
223
+ repo_uri, current_tag = current_image.rsplit(':', 1)
224
+ else:
225
+ repo_uri, current_tag = current_image, "latest"
226
+
227
+ if current_tag == new_tag:
228
+ logger.info(f"No update needed. Deployment '{deployment_name}' already using tag '{new_tag}'.")
229
+ return False
230
+
231
+ # Build the new image reference using same repo
232
+ new_image = f"{repo_uri}:{new_tag}"
233
+ patch = {
234
+ "spec": {
235
+ "template": {
236
+ "spec": {
237
+ "containers": [
238
+ {"name": container_name, "image": new_image}
239
+ ]
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ logger.info(f"Updating '{deployment_name}' from '{current_image}' to '{new_image}'")
246
+ apps_v1.patch_namespaced_deployment(
247
+ name=deployment_name,
248
+ namespace=namespace,
249
+ body=patch
250
+ )
251
+
252
+ logger.info(f"Deployment '{deployment_name}' successfully updated to {new_image}")
253
+ return True
254
+
255
+ except Exception as e:
256
+ logger.error(f"Failed to update deployment '{deployment_name}': {e}")
257
+ return False
@@ -2,7 +2,9 @@
2
2
  # This class is deprecated. Please refer to aws.py
3
3
  #========================================================================
4
4
  import json
5
+ import re
5
6
  import base64
7
+ from packaging import version
6
8
  from datetime import datetime , timezone, timedelta
7
9
  import boto3
8
10
  from botocore.exceptions import ClientError
@@ -73,6 +75,50 @@ def task_get_latest_image_digest(session, resource, region, repository_name, tag
73
75
 
74
76
  raise ValueError('Unknown resource')
75
77
 
78
+
79
+ def task_get_highest_version_tag(session, resource, region, repository_name, tag_pattern=r"^v?(?P<version>\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?)$"):
80
+ """
81
+ Fetches the highest versioned tag from an ECR repository, similar to FluxCD's semver filter.
82
+
83
+ Args:
84
+ session: boto3 session
85
+ resource: AwsResources enum
86
+ region: AWS region
87
+ repository_name: Name of the ECR repository
88
+ tag_pattern: Regex pattern to extract version component (default: semver)
89
+
90
+ Returns:
91
+ The highest matching tag or None if not found.
92
+ """
93
+ if resource == AwsResources.ECR:
94
+ ecr_client = session.client('ecr', region_name=region)
95
+ try:
96
+ pattern = re.compile(tag_pattern)
97
+ paginator = ecr_client.get_paginator('describe_images')
98
+ matched_tags = []
99
+
100
+ for page in paginator.paginate(repositoryName=repository_name, filter={'tagStatus': 'TAGGED'}):
101
+ for image in page.get('imageDetails', []):
102
+ for tag in image.get('imageTags', []):
103
+ match = pattern.match(tag)
104
+ if match:
105
+ ver_str = match.group('version')
106
+ matched_tags.append((tag, version.parse(ver_str)))
107
+
108
+ if not matched_tags:
109
+ logger.info(f"No tags matching pattern '{tag_pattern}' found in repository {repository_name}.")
110
+ return None
111
+
112
+ # Sort by semantic version and pick the highest
113
+ highest_tag, _ = max(matched_tags, key=lambda t: t[1])
114
+ return highest_tag
115
+
116
+ except Exception as e:
117
+ logger.error(f"Error fetching highest version tag from {repository_name}: {e}")
118
+ return None
119
+
120
+ raise ValueError('Unknown resource')
121
+
76
122
  #========================================================================================
77
123
  # IAM Cred update
78
124
  #========================================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kubernetes-watch
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary:
5
5
  Author: bmotevalli
6
6
  Author-email: b.motevalli@gmail.com
@@ -15,7 +15,7 @@ Requires-Dist: boto3 (>=1.34.68,<2.0.0)
15
15
  Requires-Dist: humps (>=0.2.2,<0.3.0)
16
16
  Requires-Dist: hvac (>=2.1.0,<3.0.0)
17
17
  Requires-Dist: kubernetes (>=29.0.0,<30.0.0)
18
- Requires-Dist: prefect (>=3.4.17,<4.0.0)
18
+ Requires-Dist: prefect (==3.4.18)
19
19
  Requires-Dist: psycopg2 (>=2.9.10,<3.0.0)
20
20
  Requires-Dist: pydantic (>=2.11.7,<3.0.0)
21
21
  Requires-Dist: requests (>=2.32.3,<3.0.0)
@@ -9,7 +9,7 @@ kube_watch/models/common.py,sha256=FQktpX552zSCigMxEzm4S07SvrHv5RA7YwVJHgv7uuI,5
9
9
  kube_watch/models/workflow.py,sha256=ZFBMz_LmYgROcbz2amSvms38K770njnyZC6h1bpTXGU,1634
10
10
  kube_watch/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  kube_watch/modules/clusters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- kube_watch/modules/clusters/kube.py,sha256=_skDVeKIQcZ0nC9JLY0oaFmaeEVRHS-Xbrxh1HR2t9Y,7582
12
+ kube_watch/modules/clusters/kube.py,sha256=ZvWpLrdgftLHexuigil8aa23BjqTZDmyeb6ZCAW6Y7w,10205
13
13
  kube_watch/modules/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  kube_watch/modules/database/model.py,sha256=MWG9UW6g0KuBzW6MjzPBtknAk7GmuncQrdAq6HHarTo,207
15
15
  kube_watch/modules/database/postgre.py,sha256=1Sq2YFwgCJM_FWKabV2S1bFAIl2GBwytTtPCuLfVhu8,8182
@@ -22,7 +22,7 @@ kube_watch/modules/logic/trasnform.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
22
22
  kube_watch/modules/mock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  kube_watch/modules/mock/mock_generator.py,sha256=BKKQFCxxQgFW_GFgeIbkyIbuNU4328xTTaFfTwFLsS8,1262
24
24
  kube_watch/modules/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- kube_watch/modules/providers/aws.py,sha256=oly88Bihn-vR0NdOoCT-P1MNgPw8FLNfS1Ha4hc_4bc,8375
25
+ kube_watch/modules/providers/aws.py,sha256=qKL7oGKeKOUO5AQRQqcrR35kzOpq42uNXuXumeU-gtw,10224
26
26
  kube_watch/modules/providers/git.py,sha256=5d7vkeTbVD_XN-mpCoPTRmM6lwgrwkDwcCIsjqYwmaQ,3778
27
27
  kube_watch/modules/providers/github.py,sha256=eQY8sLy2U6bOWMpFxA73DFCPVuswhTXSG25KmYSuo5s,5212
28
28
  kube_watch/modules/providers/vault.py,sha256=etzzHbTrUDsTUpeUN-xg0Xh8ulqC0-1FA3tHRZinIOo,7193
@@ -30,7 +30,7 @@ kube_watch/standalone/metarecogen/ckan_to_gn.py,sha256=LWd7ikyxRIC1IGt6CtALnDOEo
30
30
  kube_watch/watch/__init__.py,sha256=9KE0Sf1nLUTNaFvXbiQCgf11vpG8Xgmb5ddeMAmak3Q,88
31
31
  kube_watch/watch/helpers.py,sha256=8BQnQ6AeLHs0JEq54iKYDvWURb1F-kROJxwIcl_nv_Y,6276
32
32
  kube_watch/watch/workflow.py,sha256=CaXHFuEWVsFjBv5dU4IfVMeTlGJWyKaE1But9-YzVWk,9769
33
- kubernetes_watch-0.1.10.dist-info/LICENSE,sha256=_H2QdL-2dXbivDmOpJ11DnqJewSFhSJwGpHx_WAE-CA,1075
34
- kubernetes_watch-0.1.10.dist-info/METADATA,sha256=Fof5Cg1A--c2oCQ8VCrNb_GAeqw1rKjHMltZzjXurLY,5607
35
- kubernetes_watch-0.1.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
- kubernetes_watch-0.1.10.dist-info/RECORD,,
33
+ kubernetes_watch-0.1.12.dist-info/LICENSE,sha256=_H2QdL-2dXbivDmOpJ11DnqJewSFhSJwGpHx_WAE-CA,1075
34
+ kubernetes_watch-0.1.12.dist-info/METADATA,sha256=zG9wEStvXgfYthz6Aa5i05Oyoh7ygmIMWtI8kdzRM00,5600
35
+ kubernetes_watch-0.1.12.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
+ kubernetes_watch-0.1.12.dist-info/RECORD,,