k8s-helper-cli 0.2.7__tar.gz → 0.3.0__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.
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/PKG-INFO +10 -1
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/README.md +9 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/pyproject.toml +1 -1
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper/__init__.py +1 -1
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper/cli.py +111 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper/core.py +144 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper_cli.egg-info/PKG-INFO +10 -1
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/LICENSE +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/setup.cfg +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper/config.py +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper/utils.py +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper_cli.egg-info/SOURCES.txt +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper_cli.egg-info/dependency_links.txt +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper_cli.egg-info/entry_points.txt +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper_cli.egg-info/requires.txt +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper_cli.egg-info/top_level.txt +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/tests/test_core.py +0 -0
- {k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/tests/test_integration.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: k8s-helper-cli
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.0
|
4
4
|
Summary: A simplified Python wrapper for common Kubernetes operations
|
5
5
|
Author-email: Harshit Chatterjee <harshitchatterjee50@gmail.com>
|
6
6
|
License-Expression: MIT
|
@@ -573,6 +573,15 @@ k8s-helper create-deployment my-app nginx:latest --replicas 3 --namespace my-nam
|
|
573
573
|
# Scale a deployment
|
574
574
|
k8s-helper scale-deployment my-app --replicas 5 --namespace my-namespace
|
575
575
|
|
576
|
+
# Perform a rolling update
|
577
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --namespace my-namespace
|
578
|
+
|
579
|
+
# Rolling update with multiple changes
|
580
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --replicas 3 --env "ENV=prod" --namespace my-namespace
|
581
|
+
|
582
|
+
# Rolling update with status monitoring
|
583
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --show-status --namespace my-namespace
|
584
|
+
|
576
585
|
# Delete a deployment
|
577
586
|
k8s-helper delete-deployment my-app --namespace my-namespace
|
578
587
|
|
@@ -546,6 +546,15 @@ k8s-helper create-deployment my-app nginx:latest --replicas 3 --namespace my-nam
|
|
546
546
|
# Scale a deployment
|
547
547
|
k8s-helper scale-deployment my-app --replicas 5 --namespace my-namespace
|
548
548
|
|
549
|
+
# Perform a rolling update
|
550
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --namespace my-namespace
|
551
|
+
|
552
|
+
# Rolling update with multiple changes
|
553
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --replicas 3 --env "ENV=prod" --namespace my-namespace
|
554
|
+
|
555
|
+
# Rolling update with status monitoring
|
556
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --show-status --namespace my-namespace
|
557
|
+
|
549
558
|
# Delete a deployment
|
550
559
|
k8s-helper delete-deployment my-app --namespace my-namespace
|
551
560
|
|
@@ -175,6 +175,117 @@ def scale_deployment(
|
|
175
175
|
console.print(f"❌ Failed to scale deployment {name}")
|
176
176
|
|
177
177
|
|
178
|
+
@app.command()
|
179
|
+
def rolling_update(
|
180
|
+
name: str = typer.Argument(..., help="Deployment name"),
|
181
|
+
image: Optional[str] = typer.Option(None, "--image", "-i", help="New container image"),
|
182
|
+
replicas: Optional[int] = typer.Option(None, "--replicas", "-r", help="New number of replicas"),
|
183
|
+
port: Optional[int] = typer.Option(None, "--port", "-p", help="New container port"),
|
184
|
+
env: Optional[str] = typer.Option(None, "--env", "-e", help="New environment variables (KEY1=value1,KEY2=value2)"),
|
185
|
+
labels: Optional[str] = typer.Option(None, "--labels", "-l", help="New labels (key1=value1,key2=value2)"),
|
186
|
+
namespace: Optional[str] = namespace_option,
|
187
|
+
wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for rollout to complete"),
|
188
|
+
timeout: int = typer.Option(300, "--timeout", "-t", help="Timeout in seconds for rollout completion"),
|
189
|
+
show_status: bool = typer.Option(False, "--show-status", "-s", help="Show rollout status during update")
|
190
|
+
):
|
191
|
+
"""Perform a rolling update on a deployment"""
|
192
|
+
if not validate_name(name):
|
193
|
+
console.print(f"❌ Invalid deployment name: {name}")
|
194
|
+
return
|
195
|
+
|
196
|
+
# Validate that at least one parameter is provided
|
197
|
+
if not any([image, replicas is not None, port, env, labels]):
|
198
|
+
console.print("❌ At least one update parameter must be provided")
|
199
|
+
console.print(" Use --image, --replicas, --port, --env, or --labels")
|
200
|
+
return
|
201
|
+
|
202
|
+
# Validate image if provided
|
203
|
+
if image and not validate_image(image):
|
204
|
+
console.print(f"❌ Invalid image name: {image}")
|
205
|
+
return
|
206
|
+
|
207
|
+
# Parse environment variables and labels
|
208
|
+
env_vars = parse_env_vars(env) if env else None
|
209
|
+
label_dict = parse_labels(labels) if labels else None
|
210
|
+
|
211
|
+
ns = namespace or get_config().get_namespace()
|
212
|
+
client = K8sClient(namespace=ns)
|
213
|
+
|
214
|
+
# Show what will be updated
|
215
|
+
console.print(f"🔄 Performing rolling update on deployment: {name}")
|
216
|
+
console.print(f"📍 Namespace: {ns}")
|
217
|
+
|
218
|
+
updates = []
|
219
|
+
if image:
|
220
|
+
updates.append(f"🖼️ Image: {image}")
|
221
|
+
if replicas is not None:
|
222
|
+
updates.append(f"📊 Replicas: {replicas}")
|
223
|
+
if port:
|
224
|
+
updates.append(f"🚪 Port: {port}")
|
225
|
+
if env_vars:
|
226
|
+
updates.append(f"🔧 Environment variables: {list(env_vars.keys())}")
|
227
|
+
if label_dict:
|
228
|
+
updates.append(f"🏷️ Labels: {list(label_dict.keys())}")
|
229
|
+
|
230
|
+
console.print("📋 Updates to apply:")
|
231
|
+
for update in updates:
|
232
|
+
console.print(f" • {update}")
|
233
|
+
|
234
|
+
# Perform the rolling update
|
235
|
+
with console.status(f"Applying rolling update to {name}..."):
|
236
|
+
success = client.rolling_update_deployment(
|
237
|
+
name=name,
|
238
|
+
image=image,
|
239
|
+
replicas=replicas,
|
240
|
+
env_vars=env_vars,
|
241
|
+
labels=label_dict,
|
242
|
+
container_port=port
|
243
|
+
)
|
244
|
+
|
245
|
+
if not success:
|
246
|
+
console.print(f"❌ Failed to start rolling update for deployment {name}")
|
247
|
+
return
|
248
|
+
|
249
|
+
console.print(f"✅ Rolling update initiated for deployment {name}")
|
250
|
+
|
251
|
+
if wait:
|
252
|
+
console.print(f"⏳ Waiting for rollout to complete (timeout: {timeout}s)...")
|
253
|
+
|
254
|
+
if show_status:
|
255
|
+
# Show rolling status updates
|
256
|
+
start_time = time.time()
|
257
|
+
while time.time() - start_time < timeout:
|
258
|
+
status = client.get_rollout_status(name)
|
259
|
+
if status:
|
260
|
+
console.print(f"📊 Status: {status['ready_replicas']}/{status['desired_replicas']} ready, "
|
261
|
+
f"{status['updated_replicas']} updated, {status['available_replicas']} available")
|
262
|
+
|
263
|
+
# Check if rollout is complete
|
264
|
+
if (status['ready_replicas'] == status['desired_replicas'] and
|
265
|
+
status['updated_replicas'] == status['desired_replicas'] and
|
266
|
+
status['available_replicas'] == status['desired_replicas']):
|
267
|
+
break
|
268
|
+
|
269
|
+
time.sleep(3)
|
270
|
+
|
271
|
+
# Wait for rollout completion
|
272
|
+
if client.wait_for_rollout_complete(name, timeout):
|
273
|
+
console.print("✅ Rolling update completed successfully!")
|
274
|
+
|
275
|
+
# Show final status
|
276
|
+
final_status = client.get_rollout_status(name)
|
277
|
+
if final_status:
|
278
|
+
console.print(f"📊 Final status: {final_status['ready_replicas']}/{final_status['desired_replicas']} replicas ready")
|
279
|
+
|
280
|
+
console.print(f"🎉 Deployment {name} is now running with the updated configuration")
|
281
|
+
else:
|
282
|
+
console.print(f"❌ Rolling update timed out after {timeout} seconds")
|
283
|
+
console.print("💡 Check rollout status with: kubectl rollout status deployment/{name}")
|
284
|
+
else:
|
285
|
+
console.print(f"💡 Monitor rollout progress with: kubectl rollout status deployment/{name}")
|
286
|
+
console.print(f"💡 Or use: k8s-helper rolling-update {name} --show-status --wait")
|
287
|
+
|
288
|
+
|
178
289
|
@app.command()
|
179
290
|
def list_deployments(
|
180
291
|
namespace: Optional[str] = namespace_option,
|
@@ -834,6 +834,150 @@ class K8sClient:
|
|
834
834
|
print(f"❌ Error scaling deployment '{name}': {e}")
|
835
835
|
return False
|
836
836
|
|
837
|
+
def rolling_update_deployment(self, name: str, image: str = None,
|
838
|
+
replicas: int = None, env_vars: Optional[Dict[str, str]] = None,
|
839
|
+
labels: Optional[Dict[str, str]] = None,
|
840
|
+
container_port: int = None) -> bool:
|
841
|
+
"""Perform a rolling update on a deployment
|
842
|
+
|
843
|
+
Args:
|
844
|
+
name: Deployment name
|
845
|
+
image: New container image (optional)
|
846
|
+
replicas: New replica count (optional)
|
847
|
+
env_vars: New environment variables (optional)
|
848
|
+
labels: New labels (optional)
|
849
|
+
container_port: New container port (optional)
|
850
|
+
|
851
|
+
Returns:
|
852
|
+
True if successful, False otherwise
|
853
|
+
"""
|
854
|
+
try:
|
855
|
+
# Get current deployment
|
856
|
+
deployment = self.apps_v1.read_namespaced_deployment(
|
857
|
+
name=name,
|
858
|
+
namespace=self.namespace
|
859
|
+
)
|
860
|
+
|
861
|
+
# Update image if provided
|
862
|
+
if image:
|
863
|
+
for container in deployment.spec.template.spec.containers:
|
864
|
+
if container.name == name: # Update main container
|
865
|
+
container.image = image
|
866
|
+
break
|
867
|
+
|
868
|
+
# Update replicas if provided
|
869
|
+
if replicas is not None:
|
870
|
+
deployment.spec.replicas = replicas
|
871
|
+
|
872
|
+
# Update environment variables if provided
|
873
|
+
if env_vars:
|
874
|
+
for container in deployment.spec.template.spec.containers:
|
875
|
+
if container.name == name: # Update main container
|
876
|
+
env = [client.V1EnvVar(name=k, value=v) for k, v in env_vars.items()]
|
877
|
+
container.env = env
|
878
|
+
break
|
879
|
+
|
880
|
+
# Update labels if provided
|
881
|
+
if labels:
|
882
|
+
if deployment.spec.template.metadata.labels:
|
883
|
+
deployment.spec.template.metadata.labels.update(labels)
|
884
|
+
else:
|
885
|
+
deployment.spec.template.metadata.labels = labels
|
886
|
+
|
887
|
+
# Also update selector labels if they match
|
888
|
+
if deployment.spec.selector.match_labels:
|
889
|
+
deployment.spec.selector.match_labels.update(labels)
|
890
|
+
|
891
|
+
# Update container port if provided
|
892
|
+
if container_port:
|
893
|
+
for container in deployment.spec.template.spec.containers:
|
894
|
+
if container.name == name: # Update main container
|
895
|
+
if container.ports:
|
896
|
+
container.ports[0].container_port = container_port
|
897
|
+
else:
|
898
|
+
container.ports = [client.V1ContainerPort(container_port=container_port)]
|
899
|
+
break
|
900
|
+
|
901
|
+
# Apply the rolling update
|
902
|
+
self.apps_v1.patch_namespaced_deployment(
|
903
|
+
name=name,
|
904
|
+
namespace=self.namespace,
|
905
|
+
body=deployment
|
906
|
+
)
|
907
|
+
|
908
|
+
return True
|
909
|
+
except ApiException as e:
|
910
|
+
print(f"❌ Error performing rolling update on deployment '{name}': {e}")
|
911
|
+
return False
|
912
|
+
|
913
|
+
def wait_for_rollout_complete(self, name: str, timeout: int = 300) -> bool:
|
914
|
+
"""Wait for a deployment rollout to complete
|
915
|
+
|
916
|
+
Args:
|
917
|
+
name: Deployment name
|
918
|
+
timeout: Timeout in seconds
|
919
|
+
|
920
|
+
Returns:
|
921
|
+
True if rollout completed successfully, False otherwise
|
922
|
+
"""
|
923
|
+
try:
|
924
|
+
start_time = time.time()
|
925
|
+
while time.time() - start_time < timeout:
|
926
|
+
deployment = self.apps_v1.read_namespaced_deployment(
|
927
|
+
name=name,
|
928
|
+
namespace=self.namespace
|
929
|
+
)
|
930
|
+
|
931
|
+
# Check if rollout is complete
|
932
|
+
if (deployment.status.ready_replicas == deployment.spec.replicas and
|
933
|
+
deployment.status.updated_replicas == deployment.spec.replicas and
|
934
|
+
deployment.status.available_replicas == deployment.spec.replicas):
|
935
|
+
return True
|
936
|
+
|
937
|
+
time.sleep(2)
|
938
|
+
|
939
|
+
return False
|
940
|
+
except ApiException as e:
|
941
|
+
print(f"❌ Error waiting for rollout: {e}")
|
942
|
+
return False
|
943
|
+
|
944
|
+
def get_rollout_status(self, name: str) -> Dict[str, Any]:
|
945
|
+
"""Get deployment rollout status
|
946
|
+
|
947
|
+
Args:
|
948
|
+
name: Deployment name
|
949
|
+
|
950
|
+
Returns:
|
951
|
+
Dictionary with rollout status information
|
952
|
+
"""
|
953
|
+
try:
|
954
|
+
deployment = self.apps_v1.read_namespaced_deployment(
|
955
|
+
name=name,
|
956
|
+
namespace=self.namespace
|
957
|
+
)
|
958
|
+
|
959
|
+
return {
|
960
|
+
'name': deployment.metadata.name,
|
961
|
+
'desired_replicas': deployment.spec.replicas,
|
962
|
+
'current_replicas': deployment.status.replicas or 0,
|
963
|
+
'updated_replicas': deployment.status.updated_replicas or 0,
|
964
|
+
'ready_replicas': deployment.status.ready_replicas or 0,
|
965
|
+
'available_replicas': deployment.status.available_replicas or 0,
|
966
|
+
'unavailable_replicas': deployment.status.unavailable_replicas or 0,
|
967
|
+
'conditions': [
|
968
|
+
{
|
969
|
+
'type': condition.type,
|
970
|
+
'status': condition.status,
|
971
|
+
'reason': condition.reason,
|
972
|
+
'message': condition.message,
|
973
|
+
'last_update_time': condition.last_update_time
|
974
|
+
} for condition in (deployment.status.conditions or [])
|
975
|
+
]
|
976
|
+
}
|
977
|
+
except ApiException as e:
|
978
|
+
print(f"❌ Error getting rollout status: {e}")
|
979
|
+
return {}
|
980
|
+
|
837
981
|
def list_deployments(self) -> List[Dict[str, Any]]:
|
838
982
|
"""List all deployments in the namespace"""
|
839
983
|
try:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: k8s-helper-cli
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.0
|
4
4
|
Summary: A simplified Python wrapper for common Kubernetes operations
|
5
5
|
Author-email: Harshit Chatterjee <harshitchatterjee50@gmail.com>
|
6
6
|
License-Expression: MIT
|
@@ -573,6 +573,15 @@ k8s-helper create-deployment my-app nginx:latest --replicas 3 --namespace my-nam
|
|
573
573
|
# Scale a deployment
|
574
574
|
k8s-helper scale-deployment my-app --replicas 5 --namespace my-namespace
|
575
575
|
|
576
|
+
# Perform a rolling update
|
577
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --namespace my-namespace
|
578
|
+
|
579
|
+
# Rolling update with multiple changes
|
580
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --replicas 3 --env "ENV=prod" --namespace my-namespace
|
581
|
+
|
582
|
+
# Rolling update with status monitoring
|
583
|
+
k8s-helper rolling-update my-app --image nginx:1.21 --show-status --namespace my-namespace
|
584
|
+
|
576
585
|
# Delete a deployment
|
577
586
|
k8s-helper delete-deployment my-app --namespace my-namespace
|
578
587
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{k8s_helper_cli-0.2.7 → k8s_helper_cli-0.3.0}/src/k8s_helper_cli.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|