kubernetes-watch 0.1.10__py3-none-any.whl → 0.1.11__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.
@@ -183,4 +183,76 @@ def has_mismatch_image_digest(repo_digest, label_selector, namespace):
183
183
  logger.info("Images are in-sync.")
184
184
  logger.info(f"Repo digest: {repo_digest}")
185
185
  logger.info(f"Curr digest: {current_image_id.split('@')[-1]}")
186
- return False
186
+ return False
187
+
188
+
189
+ def update_deployment_image_if_needed(namespace, deployment_name, container_name, new_tag):
190
+ """
191
+ Updates the deployment's container image tag only if it differs from the current one.
192
+ The repository URI is extracted automatically from the current image reference.
193
+
194
+ Args:
195
+ namespace: Kubernetes namespace where the deployment resides.
196
+ deployment_name: Name of the deployment to patch.
197
+ container_name: Name of the container inside the deployment.
198
+ new_tag: The new image tag to set, e.g. 'v1.2.3'
199
+
200
+ Returns:
201
+ True if the image was updated and rollout triggered, False otherwise.
202
+ """
203
+ try:
204
+ # Load kube config (works both in-cluster and local)
205
+ try:
206
+ config.load_incluster_config()
207
+ except config.ConfigException:
208
+ config.load_kube_config()
209
+
210
+ apps_v1 = client.AppsV1Api()
211
+
212
+ # Read the current deployment
213
+ deployment = apps_v1.read_namespaced_deployment(deployment_name, namespace)
214
+
215
+ # Find the container we want to update
216
+ containers = deployment.spec.template.spec.containers
217
+ target_container = next((c for c in containers if c.name == container_name), None)
218
+ if not target_container:
219
+ logger.error(f"Container '{container_name}' not found in deployment '{deployment_name}'.")
220
+ return False
221
+
222
+ current_image = target_container.image
223
+ if ':' in current_image:
224
+ repo_uri, current_tag = current_image.rsplit(':', 1)
225
+ else:
226
+ repo_uri, current_tag = current_image, "latest"
227
+
228
+ if current_tag == new_tag:
229
+ logger.info(f"No update needed. Deployment '{deployment_name}' already using tag '{new_tag}'.")
230
+ return False
231
+
232
+ # Build the new image reference using same repo
233
+ new_image = f"{repo_uri}:{new_tag}"
234
+ patch = {
235
+ "spec": {
236
+ "template": {
237
+ "spec": {
238
+ "containers": [
239
+ {"name": container_name, "image": new_image}
240
+ ]
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ logger.info(f"Updating '{deployment_name}' from '{current_image}' to '{new_image}'")
247
+ apps_v1.patch_namespaced_deployment(
248
+ name=deployment_name,
249
+ namespace=namespace,
250
+ body=patch
251
+ )
252
+
253
+ logger.info(f"Deployment '{deployment_name}' successfully updated to {new_image}")
254
+ return True
255
+
256
+ except Exception as e:
257
+ logger.error(f"Failed to update deployment '{deployment_name}': {e}")
258
+ return False
@@ -3,6 +3,7 @@
3
3
  #========================================================================
4
4
  import json
5
5
  import base64
6
+ from packaging import version
6
7
  from datetime import datetime , timezone, timedelta
7
8
  import boto3
8
9
  from botocore.exceptions import ClientError
@@ -73,6 +74,50 @@ def task_get_latest_image_digest(session, resource, region, repository_name, tag
73
74
 
74
75
  raise ValueError('Unknown resource')
75
76
 
77
+
78
+ def task_get_highest_version_tag(session, resource, region, repository_name, tag_pattern=r"^v?(?P<version>\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?)$"):
79
+ """
80
+ Fetches the highest versioned tag from an ECR repository, similar to FluxCD's semver filter.
81
+
82
+ Args:
83
+ session: boto3 session
84
+ resource: AwsResources enum
85
+ region: AWS region
86
+ repository_name: Name of the ECR repository
87
+ tag_pattern: Regex pattern to extract version component (default: semver)
88
+
89
+ Returns:
90
+ The highest matching tag or None if not found.
91
+ """
92
+ if resource == AwsResources.ECR:
93
+ ecr_client = session.client('ecr', region_name=region)
94
+ try:
95
+ pattern = re.compile(tag_pattern)
96
+ paginator = ecr_client.get_paginator('describe_images')
97
+ matched_tags = []
98
+
99
+ for page in paginator.paginate(repositoryName=repository_name, filter={'tagStatus': 'TAGGED'}):
100
+ for image in page.get('imageDetails', []):
101
+ for tag in image.get('imageTags', []):
102
+ match = pattern.match(tag)
103
+ if match:
104
+ ver_str = match.group('version')
105
+ matched_tags.append((tag, version.parse(ver_str)))
106
+
107
+ if not matched_tags:
108
+ logger.info(f"No tags matching pattern '{tag_pattern}' found in repository {repository_name}.")
109
+ return None
110
+
111
+ # Sort by semantic version and pick the highest
112
+ highest_tag, _ = max(matched_tags, key=lambda t: t[1])
113
+ return highest_tag
114
+
115
+ except Exception as e:
116
+ logger.error(f"Error fetching highest version tag from {repository_name}: {e}")
117
+ return None
118
+
119
+ raise ValueError('Unknown resource')
120
+
76
121
  #========================================================================================
77
122
  # IAM Cred update
78
123
  #========================================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kubernetes-watch
3
- Version: 0.1.10
3
+ Version: 0.1.11
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=Zrj_H6TyraJGY-cewlOaLVg8N9uqOjNYeAzVQA9M93A,10220
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=Cb03rLziIIJTFQAMkimXZbdU3b8RZNcazl-HI76P6yI,10214
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.11.dist-info/LICENSE,sha256=_H2QdL-2dXbivDmOpJ11DnqJewSFhSJwGpHx_WAE-CA,1075
34
+ kubernetes_watch-0.1.11.dist-info/METADATA,sha256=q5ptqoHy4u7gTORZb46MD3cINIoi05W2BtnW5zfev4U,5600
35
+ kubernetes_watch-0.1.11.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
+ kubernetes_watch-0.1.11.dist-info/RECORD,,