k8s-helper-cli 0.5.1__py3-none-any.whl → 0.5.3__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.
k8s_helper/__init__.py CHANGED
@@ -20,7 +20,7 @@ from .utils import (
20
20
  create_service_manifest
21
21
  )
22
22
 
23
- __version__ = "0.5.1"
23
+ __version__ = "0.5.3"
24
24
  __author__ = "Harshit Chatterjee"
25
25
  __email__ = "harshitchatterjee50@gmail.com"
26
26
 
k8s_helper/cli.py CHANGED
@@ -1898,7 +1898,7 @@ def setup_monitoring_stack(
1898
1898
  wait: bool = typer.Option(True, "--wait/--no-wait", help="Wait for deployments to be ready"),
1899
1899
  install_ingress: bool = typer.Option(False, "--install-ingress", help="Install ingress for external access")
1900
1900
  ):
1901
- """Deploy monitoring stack using official Helm charts (Prometheus + Grafana)"""
1901
+ """Deploy monitoring stack using official Helm charts (auto-installs Helm if needed)"""
1902
1902
 
1903
1903
  # Validate service type
1904
1904
  valid_service_types = ["NodePort", "LoadBalancer", "ClusterIP"]
@@ -1911,6 +1911,7 @@ def setup_monitoring_stack(
1911
1911
  console.print(f"🔧 Grafana service type: {grafana_service_type}")
1912
1912
  console.print(f"💾 Prometheus storage: {prometheus_storage_size}")
1913
1913
  console.print(f"💾 Grafana storage: {grafana_storage_size}")
1914
+ console.print(f"⚙️ Note: Helm will be automatically installed if not present")
1914
1915
 
1915
1916
  # Show what will be deployed
1916
1917
  console.print("\n📋 Components to deploy via Helm:")
@@ -1990,11 +1991,19 @@ def setup_monitoring_stack(
1990
1991
  else:
1991
1992
  console.print(f"❌ Failed to deploy Helm monitoring stack: {result.get('error', 'Unknown error')}")
1992
1993
 
1994
+ # Show suggestion if provided
1995
+ if result.get('suggestion'):
1996
+ console.print(f"💡 {result['suggestion']}")
1997
+
1993
1998
  console.print("\n🛠️ Troubleshooting:")
1994
- console.print(" • Ensure Helm is installed: helm version")
1995
- console.print(" • Check cluster connectivity: kubectl get nodes")
1996
- console.print(" • Verify namespace permissions")
1997
- console.print(f" • View Helm status: helm status -n {namespace} kube-prometheus-stack")
1999
+ if 'Helm' in str(result.get('error', '')):
2000
+ console.print(" • Helm installation failed - you may need to install manually")
2001
+ console.print(" • Visit: https://helm.sh/docs/intro/install/")
2002
+ console.print(" • Restart your terminal after installation")
2003
+ else:
2004
+ console.print(" • Check cluster connectivity: kubectl get nodes")
2005
+ console.print(" • Verify namespace permissions")
2006
+ console.print(f" • View Helm status: helm status -n {namespace} kube-prometheus-stack")
1998
2007
 
1999
2008
  except Exception as e:
2000
2009
  console.print(f"❌ Error setting up Helm monitoring: {e}")
@@ -2098,5 +2107,58 @@ def delete_monitoring_stack(
2098
2107
  console.print(f"❌ Error deleting Helm monitoring: {e}")
2099
2108
 
2100
2109
 
2110
+ @app.command()
2111
+ def install_helm(
2112
+ force: bool = typer.Option(False, "--force", help="Force reinstall even if Helm is already installed")
2113
+ ):
2114
+ """Install Helm automatically on your system"""
2115
+ try:
2116
+ client = K8sClient()
2117
+
2118
+ # Check if Helm is already installed
2119
+ if not force and client._check_helm_available():
2120
+ console.print("✅ Helm is already installed on your system")
2121
+
2122
+ import subprocess
2123
+ try:
2124
+ result = subprocess.run(['helm', 'version'], capture_output=True, text=True)
2125
+ console.print(f"📋 Version: {result.stdout.strip()}")
2126
+ except:
2127
+ pass
2128
+
2129
+ console.print("💡 Use --force to reinstall")
2130
+ return
2131
+
2132
+ console.print("🔧 Installing Helm automatically...")
2133
+
2134
+ with console.status("Installing Helm..."):
2135
+ result = client._install_helm_automatically()
2136
+
2137
+ if result['success']:
2138
+ console.print(f"✅ {result['message']}")
2139
+ console.print(f"📦 Installation method: {result['method']}")
2140
+
2141
+ # Test the installation
2142
+ if client._check_helm_available():
2143
+ console.print("✅ Helm installation verified successfully")
2144
+ console.print("\n🚀 You can now use Helm-based monitoring commands:")
2145
+ console.print(" • k8s-helper setup-monitoring-stack")
2146
+ console.print(" • k8s-helper monitoring-stack-status")
2147
+ console.print(" • k8s-helper delete-monitoring-stack")
2148
+ else:
2149
+ console.print("⚠️ Helm was installed but may not be in your PATH")
2150
+ console.print("💡 You may need to restart your terminal")
2151
+ else:
2152
+ console.print(f"❌ Failed to install Helm: {result.get('error', 'Unknown error')}")
2153
+ console.print("\n🛠️ Manual installation:")
2154
+ console.print(" • Windows: choco install kubernetes-helm")
2155
+ console.print(" • macOS: brew install helm")
2156
+ console.print(" • Linux: curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash")
2157
+ console.print(" • Visit: https://helm.sh/docs/intro/install/")
2158
+
2159
+ except Exception as e:
2160
+ console.print(f"❌ Error installing Helm: {e}")
2161
+
2162
+
2101
2163
  if __name__ == "__main__":
2102
2164
  app()
k8s_helper/core.py CHANGED
@@ -1694,6 +1694,261 @@ class K8sClient:
1694
1694
  except (subprocess.TimeoutExpired, FileNotFoundError):
1695
1695
  return False
1696
1696
 
1697
+ def _install_helm_automatically(self) -> Dict[str, Any]:
1698
+ """Automatically install Helm based on the operating system"""
1699
+ import subprocess
1700
+ import platform
1701
+ import os
1702
+ import tempfile
1703
+ import urllib.request
1704
+
1705
+ result = {
1706
+ 'success': False,
1707
+ 'method': None,
1708
+ 'error': None,
1709
+ 'message': None
1710
+ }
1711
+
1712
+ system = platform.system().lower()
1713
+ print(f"🔧 Attempting to install Helm on {system}...")
1714
+
1715
+ try:
1716
+ if system == 'windows':
1717
+ # Try to install via Chocolatey first, then Scoop, then direct download
1718
+ methods = [
1719
+ {
1720
+ 'name': 'Chocolatey',
1721
+ 'check_cmd': ['choco', '--version'],
1722
+ 'install_cmd': ['choco', 'install', 'kubernetes-helm', '-y']
1723
+ },
1724
+ {
1725
+ 'name': 'Scoop',
1726
+ 'check_cmd': ['scoop', '--version'],
1727
+ 'install_cmd': ['scoop', 'install', 'helm']
1728
+ }
1729
+ ]
1730
+
1731
+ for method in methods:
1732
+ try:
1733
+ # Check if package manager is available
1734
+ subprocess.run(method['check_cmd'], capture_output=True, check=True, timeout=10)
1735
+ print(f"📦 Installing Helm via {method['name']}...")
1736
+
1737
+ # Install Helm
1738
+ install_result = subprocess.run(
1739
+ method['install_cmd'],
1740
+ capture_output=True,
1741
+ text=True,
1742
+ timeout=300 # 5 minutes timeout
1743
+ )
1744
+
1745
+ if install_result.returncode == 0:
1746
+ result['success'] = True
1747
+ result['method'] = method['name']
1748
+ result['message'] = f"Helm installed successfully via {method['name']}"
1749
+ print(f"✅ {result['message']}")
1750
+ return result
1751
+ else:
1752
+ print(f"⚠️ {method['name']} installation failed: {install_result.stderr}")
1753
+
1754
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
1755
+ print(f"⚠️ {method['name']} not available")
1756
+ continue
1757
+
1758
+ # If package managers fail, try direct download
1759
+ print("📦 Attempting direct download installation...")
1760
+ result = self._install_helm_direct_download()
1761
+
1762
+ elif system == 'linux':
1763
+ # Try different Linux package managers and methods
1764
+ methods = [
1765
+ {
1766
+ 'name': 'Snap',
1767
+ 'check_cmd': ['snap', '--version'],
1768
+ 'install_cmd': ['sudo', 'snap', 'install', 'helm', '--classic']
1769
+ },
1770
+ {
1771
+ 'name': 'Script',
1772
+ 'script_url': 'https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3'
1773
+ }
1774
+ ]
1775
+
1776
+ # Try snap first
1777
+ try:
1778
+ subprocess.run(['snap', '--version'], capture_output=True, check=True, timeout=10)
1779
+ print("📦 Installing Helm via Snap...")
1780
+
1781
+ install_result = subprocess.run(
1782
+ ['sudo', 'snap', 'install', 'helm', '--classic'],
1783
+ capture_output=True,
1784
+ text=True,
1785
+ timeout=300
1786
+ )
1787
+
1788
+ if install_result.returncode == 0:
1789
+ result['success'] = True
1790
+ result['method'] = 'Snap'
1791
+ result['message'] = "Helm installed successfully via Snap"
1792
+ print(f"✅ {result['message']}")
1793
+ return result
1794
+
1795
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
1796
+ print("⚠️ Snap not available")
1797
+
1798
+ # Try installation script
1799
+ print("📦 Installing Helm via official installation script...")
1800
+ try:
1801
+ script_result = subprocess.run([
1802
+ 'bash', '-c',
1803
+ 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash'
1804
+ ], capture_output=True, text=True, timeout=300)
1805
+
1806
+ if script_result.returncode == 0:
1807
+ result['success'] = True
1808
+ result['method'] = 'Installation Script'
1809
+ result['message'] = "Helm installed successfully via installation script"
1810
+ print(f"✅ {result['message']}")
1811
+ return result
1812
+ else:
1813
+ print(f"⚠️ Script installation failed: {script_result.stderr}")
1814
+
1815
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
1816
+ print("⚠️ Script installation failed")
1817
+
1818
+ elif system == 'darwin': # macOS
1819
+ # Try Homebrew
1820
+ try:
1821
+ subprocess.run(['brew', '--version'], capture_output=True, check=True, timeout=10)
1822
+ print("📦 Installing Helm via Homebrew...")
1823
+
1824
+ install_result = subprocess.run(
1825
+ ['brew', 'install', 'helm'],
1826
+ capture_output=True,
1827
+ text=True,
1828
+ timeout=300
1829
+ )
1830
+
1831
+ if install_result.returncode == 0:
1832
+ result['success'] = True
1833
+ result['method'] = 'Homebrew'
1834
+ result['message'] = "Helm installed successfully via Homebrew"
1835
+ print(f"✅ {result['message']}")
1836
+ return result
1837
+
1838
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
1839
+ print("⚠️ Homebrew not available, trying installation script...")
1840
+
1841
+ # Fallback to installation script
1842
+ try:
1843
+ script_result = subprocess.run([
1844
+ 'bash', '-c',
1845
+ 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash'
1846
+ ], capture_output=True, text=True, timeout=300)
1847
+
1848
+ if script_result.returncode == 0:
1849
+ result['success'] = True
1850
+ result['method'] = 'Installation Script'
1851
+ result['message'] = "Helm installed successfully via installation script"
1852
+ print(f"✅ {result['message']}")
1853
+ return result
1854
+
1855
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
1856
+ print("⚠️ Script installation failed")
1857
+
1858
+ # If we reach here, all methods failed
1859
+ result['error'] = f"Could not install Helm automatically on {system}"
1860
+ print(f"❌ {result['error']}")
1861
+
1862
+ except Exception as e:
1863
+ result['error'] = f"Error during Helm installation: {str(e)}"
1864
+ print(f"❌ {result['error']}")
1865
+
1866
+ return result
1867
+
1868
+ def _install_helm_direct_download(self) -> Dict[str, Any]:
1869
+ """Install Helm via direct download (Windows fallback)"""
1870
+ import subprocess
1871
+ import tempfile
1872
+ import zipfile
1873
+ import urllib.request
1874
+ import os
1875
+ import platform
1876
+
1877
+ result = {
1878
+ 'success': False,
1879
+ 'method': 'Direct Download',
1880
+ 'error': None,
1881
+ 'message': None
1882
+ }
1883
+
1884
+ try:
1885
+ # Determine architecture
1886
+ arch = platform.machine().lower()
1887
+ if arch == 'amd64' or arch == 'x86_64':
1888
+ arch = 'amd64'
1889
+ elif arch == 'arm64':
1890
+ arch = 'arm64'
1891
+ else:
1892
+ arch = 'amd64' # Default fallback
1893
+
1894
+ # Download URL for latest Helm
1895
+ download_url = f"https://get.helm.sh/helm-v3.13.0-windows-{arch}.zip"
1896
+
1897
+ with tempfile.TemporaryDirectory() as temp_dir:
1898
+ zip_path = os.path.join(temp_dir, "helm.zip")
1899
+
1900
+ print(f"📥 Downloading Helm from {download_url}...")
1901
+ urllib.request.urlretrieve(download_url, zip_path)
1902
+
1903
+ print("📦 Extracting Helm...")
1904
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
1905
+ zip_ref.extractall(temp_dir)
1906
+
1907
+ # Find helm.exe
1908
+ helm_exe = None
1909
+ for root, dirs, files in os.walk(temp_dir):
1910
+ if 'helm.exe' in files:
1911
+ helm_exe = os.path.join(root, 'helm.exe')
1912
+ break
1913
+
1914
+ if helm_exe:
1915
+ # Try to move to a directory in PATH
1916
+ target_locations = [
1917
+ os.path.expanduser("~/bin"),
1918
+ "C:\\Program Files\\Helm",
1919
+ os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Microsoft', 'WindowsApps')
1920
+ ]
1921
+
1922
+ for target_dir in target_locations:
1923
+ try:
1924
+ os.makedirs(target_dir, exist_ok=True)
1925
+ target_path = os.path.join(target_dir, 'helm.exe')
1926
+
1927
+ import shutil
1928
+ shutil.copy2(helm_exe, target_path)
1929
+
1930
+ # Test if it works
1931
+ test_result = subprocess.run([target_path, 'version'],
1932
+ capture_output=True, timeout=10)
1933
+ if test_result.returncode == 0:
1934
+ result['success'] = True
1935
+ result['message'] = f"Helm installed to {target_path}"
1936
+ print(f"✅ {result['message']}")
1937
+ print(f"💡 Please add {target_dir} to your PATH if not already present")
1938
+ return result
1939
+
1940
+ except (OSError, subprocess.TimeoutExpired):
1941
+ continue
1942
+
1943
+ result['error'] = "Downloaded Helm but could not install to PATH"
1944
+ else:
1945
+ result['error'] = "Could not find helm.exe in downloaded archive"
1946
+
1947
+ except Exception as e:
1948
+ result['error'] = f"Direct download failed: {str(e)}"
1949
+
1950
+ return result
1951
+
1697
1952
  def _install_kube_state_metrics(self, namespace: str) -> Dict[str, Any]:
1698
1953
  """Install kube-state-metrics using Helm if available, or manual YAML if not"""
1699
1954
  import subprocess
@@ -3100,14 +3355,29 @@ scrape_configs:
3100
3355
  import os
3101
3356
 
3102
3357
  try:
3103
- # Check if Helm is available
3104
- try:
3105
- result = subprocess.run(['helm', 'version'], capture_output=True, text=True, check=True)
3106
- except (subprocess.CalledProcessError, FileNotFoundError):
3107
- return {
3108
- 'success': False,
3109
- 'error': 'Helm is not installed or not in PATH. Please install Helm first.'
3110
- }
3358
+ # Check if Helm is available, and install if not
3359
+ if not self._check_helm_available():
3360
+ print("🔧 Helm not found. Attempting automatic installation...")
3361
+ install_result = self._install_helm_automatically()
3362
+
3363
+ if not install_result['success']:
3364
+ return {
3365
+ 'success': False,
3366
+ 'error': f"Helm is required but could not be installed automatically. {install_result.get('error', '')}",
3367
+ 'suggestion': 'Please install Helm manually: https://helm.sh/docs/intro/install/'
3368
+ }
3369
+ else:
3370
+ print(f"✅ {install_result['message']}")
3371
+
3372
+ # Verify installation worked
3373
+ if not self._check_helm_available():
3374
+ return {
3375
+ 'success': False,
3376
+ 'error': 'Helm was installed but is not accessible. You may need to restart your terminal or add Helm to your PATH.',
3377
+ 'suggestion': 'Please restart your terminal and try again, or install Helm manually.'
3378
+ }
3379
+
3380
+ print("✅ Helm is available")
3111
3381
 
3112
3382
  # Create namespace if it doesn't exist
3113
3383
  try:
@@ -3127,14 +3397,14 @@ scrape_configs:
3127
3397
  subprocess.run([
3128
3398
  'helm', 'repo', 'add', 'prometheus-community',
3129
3399
  'https://prometheus-community.github.io/helm-charts'
3130
- ], check=True, capture_output=True)
3400
+ ], check=True, capture_output=True, text=True)
3131
3401
 
3132
- subprocess.run(['helm', 'repo', 'update'], check=True, capture_output=True)
3402
+ subprocess.run(['helm', 'repo', 'update'], check=True, capture_output=True, text=True)
3133
3403
  print("✅ Helm repository added and updated")
3134
3404
  except subprocess.CalledProcessError as e:
3135
3405
  return {
3136
3406
  'success': False,
3137
- 'error': f'Failed to add Helm repository: {e.stderr.decode() if e.stderr else str(e)}'
3407
+ 'error': f'Failed to add Helm repository: {e.stderr if e.stderr else str(e)}'
3138
3408
  }
3139
3409
 
3140
3410
  # Create Helm values file
@@ -3303,7 +3573,7 @@ scrape_configs:
3303
3573
  os.unlink(values_file)
3304
3574
 
3305
3575
  except subprocess.CalledProcessError as e:
3306
- error_msg = e.stderr.decode() if e.stderr else str(e)
3576
+ error_msg = e.stderr if e.stderr else str(e)
3307
3577
  return {
3308
3578
  'success': False,
3309
3579
  'error': f'Helm installation failed: {error_msg}'
@@ -3408,7 +3678,7 @@ scrape_configs:
3408
3678
  }
3409
3679
 
3410
3680
  except subprocess.CalledProcessError as e:
3411
- error_msg = e.stderr.decode() if e.stderr else str(e)
3681
+ error_msg = e.stderr if e.stderr else str(e)
3412
3682
  return {
3413
3683
  'success': False,
3414
3684
  'error': f'Failed to uninstall Helm release: {error_msg}'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: k8s-helper-cli
3
- Version: 0.5.1
3
+ Version: 0.5.3
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
@@ -0,0 +1,11 @@
1
+ k8s_helper/__init__.py,sha256=3GNylm6r22pm4iWnjRfi_QLWcPBP9GL0NqZSTtFnLdc,2666
2
+ k8s_helper/cli.py,sha256=3eP3J7srPIM0bm_G5RxdU_RvtDwJHLX2xBguF2EmXtk,95115
3
+ k8s_helper/config.py,sha256=P7YdfyvCHprrNs2J9DRb3RrClylfTTh5hfTtDzLug0A,6867
4
+ k8s_helper/core.py,sha256=0a4juGOteVZ3xwPqhq8vA3Dai63El1nCHo4D9tt7UMM,160443
5
+ k8s_helper/utils.py,sha256=wYgTd5ktyuI-EiVcfW7FrxA7MzXY5odrEKQgmMVdueY,9496
6
+ k8s_helper_cli-0.5.3.dist-info/licenses/LICENSE,sha256=tXPvVl3gLVc6e0qCEoLH9KjeA7z4JVL78UybpvGtBCw,1096
7
+ k8s_helper_cli-0.5.3.dist-info/METADATA,sha256=K-zPIghmapn0gBpCr0AAxR3Uulbu1ww3z_xxTbNIKEo,30789
8
+ k8s_helper_cli-0.5.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ k8s_helper_cli-0.5.3.dist-info/entry_points.txt,sha256=IoCMWUZ6mn90LwzQzEy5YkWOwvogDdZ6ycqUWAzCFTQ,50
10
+ k8s_helper_cli-0.5.3.dist-info/top_level.txt,sha256=x9A1jflyer-z2cFnkqk5B42juoH2q0fy5hkT9upsTG8,11
11
+ k8s_helper_cli-0.5.3.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- k8s_helper/__init__.py,sha256=Rm3LMlyreNv628XF3jVu7wszfxHHL0JWZKzmHFmq_D0,2666
2
- k8s_helper/cli.py,sha256=U4oPgjykRmkBvPli7jZLVvZrb13L6-wjV0hd-RQZN9g,92124
3
- k8s_helper/config.py,sha256=P7YdfyvCHprrNs2J9DRb3RrClylfTTh5hfTtDzLug0A,6867
4
- k8s_helper/core.py,sha256=P6nvVPuW44Jdvkm572__d4nycLin6cl8obZ-XqzuTY4,147614
5
- k8s_helper/utils.py,sha256=wYgTd5ktyuI-EiVcfW7FrxA7MzXY5odrEKQgmMVdueY,9496
6
- k8s_helper_cli-0.5.1.dist-info/licenses/LICENSE,sha256=tXPvVl3gLVc6e0qCEoLH9KjeA7z4JVL78UybpvGtBCw,1096
7
- k8s_helper_cli-0.5.1.dist-info/METADATA,sha256=Z2Il7mbkN4p29oc_83mIjxD3l13IPTihkzo5d0JPIT4,30789
8
- k8s_helper_cli-0.5.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- k8s_helper_cli-0.5.1.dist-info/entry_points.txt,sha256=IoCMWUZ6mn90LwzQzEy5YkWOwvogDdZ6ycqUWAzCFTQ,50
10
- k8s_helper_cli-0.5.1.dist-info/top_level.txt,sha256=x9A1jflyer-z2cFnkqk5B42juoH2q0fy5hkT9upsTG8,11
11
- k8s_helper_cli-0.5.1.dist-info/RECORD,,