Skip to content

Commit 4345b68

Browse files
authored
Merge pull request #19 from vantagecompute/feat/kubeconfig_from_string
feat: support kubeconfig from string
2 parents 4e16800 + b441e77 commit 4345b68

File tree

8 files changed

+1663
-51
lines changed

8 files changed

+1663
-51
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ RUN mkdir -p /build/helm_sdkpy/_lib/linux-amd64 && \
4242
cd shim && \
4343
go build -buildmode=c-shared \
4444
-o /build/helm_sdkpy/_lib/linux-amd64/libhelm_sdkpy.so \
45-
main.go
45+
.
4646

4747
# Verify the shared library was built
4848
RUN ls -lh /build/helm_sdkpy/_lib/linux-amd64/libhelm_sdkpy.so && \

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,50 @@ chart_path="https://charts.bitnami.com/bitnami/nginx-15.0.0.tgz"
138138
chart_path="https://example.com/charts/myapp-1.2.3.tgz"
139139
```
140140

141+
## 🔧 Kubeconfig Configuration
142+
143+
helm-sdkpy provides flexible options for configuring Kubernetes cluster access:
144+
145+
### Default Kubeconfig
146+
```python
147+
# Uses $KUBECONFIG env var or ~/.kube/config
148+
config = helm_sdkpy.Configuration(namespace="default")
149+
```
150+
151+
### File Path
152+
```python
153+
# Explicit path to kubeconfig file
154+
config = helm_sdkpy.Configuration(
155+
namespace="default",
156+
kubeconfig="/path/to/kubeconfig.yaml"
157+
)
158+
```
159+
160+
### YAML String
161+
Pass kubeconfig content directly as a string - useful for dynamic configurations, secrets, or CI/CD environments:
162+
```python
163+
# Kubeconfig from environment variable
164+
kubeconfig_content = os.environ.get("KUBECONFIG_CONTENT")
165+
166+
# Or read from a secret, API response, etc.
167+
config = helm_sdkpy.Configuration(
168+
namespace="default",
169+
kubeconfig=kubeconfig_content # YAML string auto-detected
170+
)
171+
```
172+
173+
### Specific Context
174+
```python
175+
# Use a specific context from multi-cluster kubeconfig
176+
config = helm_sdkpy.Configuration(
177+
namespace="production",
178+
kubeconfig="/path/to/kubeconfig.yaml",
179+
kubecontext="production-cluster"
180+
)
181+
```
182+
183+
See [examples/kubeconfig_usage.py](examples/kubeconfig_usage.py) for more detailed examples.
184+
141185
## 📖 API Overview
142186

143187
### Core Actions (All Async)

examples/kubeconfig_usage.py

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2025 Vantage Compute
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""
17+
Kubeconfig Usage Example
18+
19+
This example demonstrates different ways to configure kubeconfig in helm-sdkpy:
20+
1. Using the default kubeconfig (~/.kube/config or $KUBECONFIG)
21+
2. Using an explicit file path
22+
3. Using a kubeconfig YAML string directly
23+
24+
The kubeconfig string approach is particularly useful when:
25+
- Working with dynamic or programmatically generated configurations
26+
- Retrieving kubeconfig from Kubernetes secrets, environment variables, or APIs
27+
- Working in containerized environments where file access may be limited
28+
"""
29+
30+
import asyncio
31+
import os
32+
from pathlib import Path
33+
34+
import helm_sdkpy
35+
36+
37+
async def example_default_kubeconfig():
38+
"""Example 1: Use default kubeconfig location.
39+
40+
This uses $KUBECONFIG environment variable if set,
41+
otherwise falls back to ~/.kube/config.
42+
"""
43+
print("=" * 60)
44+
print("Example 1: Default Kubeconfig")
45+
print("=" * 60)
46+
47+
# When kubeconfig is None, uses default locations
48+
config = helm_sdkpy.Configuration(
49+
namespace="default",
50+
# kubeconfig=None uses default: $KUBECONFIG or ~/.kube/config
51+
)
52+
53+
# List releases to verify connection
54+
list_action = helm_sdkpy.List(config)
55+
releases = await list_action.run()
56+
print(f"Found {len(releases)} releases in default namespace")
57+
58+
return config
59+
60+
61+
async def example_kubeconfig_filepath():
62+
"""Example 2: Use explicit kubeconfig file path.
63+
64+
Useful when you have multiple kubeconfig files for different clusters.
65+
"""
66+
print("\n" + "=" * 60)
67+
print("Example 2: Kubeconfig from File Path")
68+
print("=" * 60)
69+
70+
# Specify an explicit path to a kubeconfig file
71+
kubeconfig_path = os.path.expanduser("~/.kube/config")
72+
73+
# Or use a different file for a specific cluster
74+
# kubeconfig_path = "/path/to/production-cluster.yaml"
75+
# kubeconfig_path = "/etc/rancher/k3s/k3s.yaml"
76+
77+
config = helm_sdkpy.Configuration(
78+
namespace="kube-system",
79+
kubeconfig=kubeconfig_path,
80+
)
81+
82+
# List releases in kube-system namespace
83+
list_action = helm_sdkpy.List(config)
84+
releases = await list_action.run()
85+
print(f"Found {len(releases)} releases in kube-system namespace")
86+
87+
return config
88+
89+
90+
async def example_kubeconfig_string():
91+
"""Example 3: Use kubeconfig YAML content as a string.
92+
93+
This is useful when:
94+
- Retrieving kubeconfig from environment variables
95+
- Loading from Kubernetes secrets
96+
- Programmatically generating configurations
97+
- Working in serverless or containerized environments
98+
"""
99+
print("\n" + "=" * 60)
100+
print("Example 3: Kubeconfig from String")
101+
print("=" * 60)
102+
103+
# Example: Read kubeconfig content from a file into a string
104+
# In practice, this could come from an environment variable,
105+
# API response, Kubernetes secret, etc.
106+
107+
kubeconfig_path = os.path.expanduser("~/.kube/config")
108+
if Path(kubeconfig_path).exists():
109+
with open(kubeconfig_path, "r") as f:
110+
kubeconfig_content = f.read()
111+
else:
112+
# Example kubeconfig YAML structure (won't work without real cluster)
113+
kubeconfig_content = """
114+
apiVersion: v1
115+
kind: Config
116+
clusters:
117+
- cluster:
118+
server: https://kubernetes.example.com:6443
119+
certificate-authority-data: <base64-encoded-ca-cert>
120+
name: my-cluster
121+
contexts:
122+
- context:
123+
cluster: my-cluster
124+
user: my-user
125+
namespace: default
126+
name: my-context
127+
current-context: my-context
128+
users:
129+
- name: my-user
130+
user:
131+
token: <your-token-here>
132+
"""
133+
134+
# Pass the YAML content directly as the kubeconfig parameter
135+
# helm-sdkpy automatically detects if it's a file path or YAML content
136+
config = helm_sdkpy.Configuration(
137+
namespace="default",
138+
kubeconfig=kubeconfig_content, # YAML string, not a file path!
139+
)
140+
141+
# List releases to verify connection
142+
list_action = helm_sdkpy.List(config)
143+
releases = await list_action.run()
144+
print(f"Found {len(releases)} releases using kubeconfig string")
145+
146+
return config
147+
148+
149+
async def example_kubeconfig_from_env():
150+
"""Example 4: Load kubeconfig from environment variable.
151+
152+
Many CI/CD systems and container orchestrators provide kubeconfig
153+
as an environment variable.
154+
"""
155+
print("\n" + "=" * 60)
156+
print("Example 4: Kubeconfig from Environment Variable")
157+
print("=" * 60)
158+
159+
# Common patterns for kubeconfig in environment variables:
160+
# - KUBECONFIG_CONTENT: Full YAML content
161+
# - KUBECONFIG_BASE64: Base64-encoded YAML content
162+
163+
import base64
164+
165+
kubeconfig_content = None
166+
167+
# Try getting YAML content directly
168+
if "KUBECONFIG_CONTENT" in os.environ:
169+
kubeconfig_content = os.environ["KUBECONFIG_CONTENT"]
170+
print("Loaded kubeconfig from KUBECONFIG_CONTENT env var")
171+
172+
# Try getting base64-encoded content (common in CI/CD)
173+
elif "KUBECONFIG_BASE64" in os.environ:
174+
kubeconfig_content = base64.b64decode(
175+
os.environ["KUBECONFIG_BASE64"]
176+
).decode("utf-8")
177+
print("Loaded kubeconfig from KUBECONFIG_BASE64 env var")
178+
179+
# Fall back to file path from KUBECONFIG env var
180+
elif "KUBECONFIG" in os.environ:
181+
kubeconfig_path = os.environ["KUBECONFIG"]
182+
print(f"Using kubeconfig file from KUBECONFIG env var: {kubeconfig_path}")
183+
config = helm_sdkpy.Configuration(
184+
namespace="default",
185+
kubeconfig=kubeconfig_path,
186+
)
187+
list_action = helm_sdkpy.List(config)
188+
releases = await list_action.run()
189+
print(f"Found {len(releases)} releases")
190+
return config
191+
192+
else:
193+
print("No kubeconfig environment variable found")
194+
print("Set KUBECONFIG_CONTENT, KUBECONFIG_BASE64, or KUBECONFIG")
195+
return None
196+
197+
# Use the content string directly
198+
config = helm_sdkpy.Configuration(
199+
namespace="default",
200+
kubeconfig=kubeconfig_content,
201+
)
202+
203+
list_action = helm_sdkpy.List(config)
204+
releases = await list_action.run()
205+
print(f"Found {len(releases)} releases")
206+
207+
return config
208+
209+
210+
async def example_kubeconfig_with_context():
211+
"""Example 5: Use kubeconfig with specific context.
212+
213+
When your kubeconfig has multiple contexts (clusters), you can
214+
specify which one to use.
215+
"""
216+
print("\n" + "=" * 60)
217+
print("Example 5: Kubeconfig with Specific Context")
218+
print("=" * 60)
219+
220+
kubeconfig_path = os.path.expanduser("~/.kube/config")
221+
222+
# Specify both the kubeconfig file and the context to use
223+
config = helm_sdkpy.Configuration(
224+
namespace="default",
225+
kubeconfig=kubeconfig_path,
226+
kubecontext="my-cluster-context", # Use a specific context
227+
)
228+
229+
print(f"Using kubeconfig: {kubeconfig_path}")
230+
print(f"Using context: my-cluster-context")
231+
232+
# This would fail if the context doesn't exist
233+
# list_action = helm_sdkpy.List(config)
234+
# releases = await list_action.run()
235+
236+
return config
237+
238+
239+
async def main():
240+
"""Run all kubeconfig usage examples."""
241+
print("helm-sdkpy Kubeconfig Usage Examples")
242+
print("=" * 60)
243+
print(f"helm-sdkpy version: {helm_sdkpy.__version__}")
244+
245+
try:
246+
print(f"Library version: {helm_sdkpy.get_version()}")
247+
except helm_sdkpy.HelmLibraryNotFound:
248+
print("\nLibrary not found - please build the library first with 'just build-lib'")
249+
return
250+
251+
# Run examples that work with available kubeconfig
252+
try:
253+
await example_default_kubeconfig()
254+
except Exception as e:
255+
print(f"Example 1 failed (expected if no cluster): {e}")
256+
257+
try:
258+
await example_kubeconfig_filepath()
259+
except Exception as e:
260+
print(f"Example 2 failed (expected if no cluster): {e}")
261+
262+
try:
263+
await example_kubeconfig_string()
264+
except Exception as e:
265+
print(f"Example 3 failed (expected if no cluster): {e}")
266+
267+
try:
268+
await example_kubeconfig_from_env()
269+
except Exception as e:
270+
print(f"Example 4 failed (expected if no env vars set): {e}")
271+
272+
print("\n" + "=" * 60)
273+
print("Summary: Kubeconfig Configuration Options")
274+
print("=" * 60)
275+
print("""
276+
Configuration(namespace, kubeconfig, kubecontext) parameters:
277+
278+
1. kubeconfig=None (default)
279+
- Uses $KUBECONFIG environment variable if set
280+
- Falls back to ~/.kube/config
281+
282+
2. kubeconfig="/path/to/config.yaml"
283+
- Uses explicit file path to kubeconfig
284+
285+
3. kubeconfig="apiVersion: v1\\nkind: Config\\n..."
286+
- YAML content passed directly as a string
287+
- Auto-detected by looking for apiVersion:, kind:, or clusters: markers
288+
- Useful for dynamic configurations, secrets, or environment variables
289+
290+
4. kubecontext="my-context"
291+
- Select a specific context from the kubeconfig
292+
- Works with both file paths and string content
293+
""")
294+
295+
296+
if __name__ == "__main__":
297+
asyncio.run(main())

0 commit comments

Comments
 (0)