Skip to content

Commit d38b3b6

Browse files
committed
feat/Add OCI Regisrty in code
1 parent d1f99f1 commit d38b3b6

File tree

2 files changed

+125
-8
lines changed

2 files changed

+125
-8
lines changed

internal/helm/install.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"helm.sh/helm/v3/pkg/cli"
1616
"helm.sh/helm/v3/pkg/downloader"
1717
"helm.sh/helm/v3/pkg/getter"
18+
"helm.sh/helm/v3/pkg/registry"
1819
"helm.sh/helm/v3/pkg/repo"
1920
)
2021

@@ -105,7 +106,14 @@ func HelmInstall(
105106
}
106107

107108
// loadChart determines the chart source and loads it appropriately
109+
// LoadChart determines the chart source and loads it appropriately
108110
func LoadChart(chartRef, repoURL, version string, settings *cli.EnvSettings) (*chart.Chart, error) {
111+
// Check if it's an OCI registry reference
112+
if strings.HasPrefix(chartRef, "oci://") {
113+
fmt.Printf("🐳 Loading OCI chart from registry...\n")
114+
return LoadOCIChart(chartRef, version, settings, false)
115+
}
116+
109117
if repoURL != "" {
110118
fmt.Printf("🌐 Loading remote chart from repository...\n")
111119
return LoadRemoteChart(chartRef, repoURL, version, settings)
@@ -119,6 +127,116 @@ func LoadChart(chartRef, repoURL, version string, settings *cli.EnvSettings) (*c
119127
return loader.Load(chartRef)
120128
}
121129

130+
// LoadOCIChart loads a chart from an OCI registry
131+
func LoadOCIChart(chartRef, version string, settings *cli.EnvSettings, debug bool) (*chart.Chart, error) {
132+
if debug {
133+
pterm.Printf("Loading OCI chart: %s (version: %s)\n", chartRef, version)
134+
}
135+
136+
// Create registry client
137+
registryClient, err := newRegistryClient(debug)
138+
if err != nil {
139+
return nil, fmt.Errorf("failed to create registry client: %w", err)
140+
}
141+
142+
// Create a temporary action configuration with registry client
143+
actionConfig := new(action.Configuration)
144+
actionConfig.RegistryClient = registryClient
145+
146+
// Initialize with default settings
147+
logFn := func(format string, v ...interface{}) {
148+
if debug {
149+
message := fmt.Sprintf(format, v...)
150+
pterm.Printfln("OCI-REGISTRY: %s", strings.TrimSpace(message))
151+
}
152+
}
153+
154+
if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), logFn); err != nil {
155+
return nil, fmt.Errorf("failed to initialize action config for OCI: %w", err)
156+
}
157+
158+
// Create pull action WITH registry client
159+
pull := action.NewPullWithOpts(action.WithConfig(&action.Configuration{
160+
RegistryClient: registryClient,
161+
}))
162+
pull.Settings = settings
163+
pull.Version = version
164+
pull.Untar = true
165+
pull.UntarDir = settings.RepositoryCache
166+
167+
// Run the pull command
168+
fmt.Printf("⬇️ Pulling OCI chart: %s...\n", chartRef)
169+
output, err := pull.Run(chartRef)
170+
if err != nil {
171+
return nil, fmt.Errorf("failed to pull OCI chart: %w", err)
172+
}
173+
174+
// Find the pulled chart - OCI pull returns the path
175+
chartPath := strings.TrimSpace(output)
176+
177+
// For OCI charts, handle the output format
178+
if strings.Contains(chartPath, "Pulled: ") {
179+
parts := strings.Split(chartPath, "Pulled: ")
180+
if len(parts) > 1 {
181+
chartPath = strings.TrimSpace(parts[1])
182+
}
183+
}
184+
185+
// Sometimes OCI charts are in subdirectories
186+
files, err := os.ReadDir(chartPath)
187+
if err == nil && len(files) == 1 && files[0].IsDir() {
188+
chartPath = filepath.Join(chartPath, files[0].Name())
189+
}
190+
191+
if debug {
192+
pterm.Printf("OCI chart downloaded to: %s\n", chartPath)
193+
}
194+
195+
fmt.Printf("📦 Loading OCI chart into memory...\n")
196+
return loader.Load(chartPath)
197+
}
198+
199+
// newRegistryClient creates a registry client for OCI operations
200+
func newRegistryClient(debug bool) (*registry.Client, error) {
201+
// Create registry client options
202+
opts := []registry.ClientOption{
203+
registry.ClientOptWriter(os.Stdout),
204+
}
205+
206+
// Try to load credentials from various locations
207+
helmConfig := helmHome()
208+
credentialFiles := []string{
209+
filepath.Join(helmConfig, "config.json"),
210+
filepath.Join(os.Getenv("HOME"), ".docker/config.json"),
211+
"/etc/docker/config.json",
212+
}
213+
214+
for _, credFile := range credentialFiles {
215+
if _, err := os.Stat(credFile); err == nil {
216+
opts = append(opts, registry.ClientOptCredentialsFile(credFile))
217+
if debug {
218+
pterm.Printf("Using credentials file: %s\n", credFile)
219+
}
220+
break
221+
}
222+
}
223+
224+
// Create and return the registry client
225+
return registry.NewClient(opts...)
226+
}
227+
228+
// helmHome gets the Helm home directory
229+
func helmHome() string {
230+
if home := os.Getenv("HELM_HOME"); home != "" {
231+
return home
232+
}
233+
if home := os.Getenv("HELM_CONFIG_HOME"); home != "" {
234+
return home
235+
}
236+
userHome, _ := os.UserHomeDir()
237+
return filepath.Join(userHome, ".helm")
238+
}
239+
122240
// loadFromLocalRepo loads a chart from a local repository
123241
func LoadFromLocalRepo(chartRef, version string, settings *cli.EnvSettings) (*chart.Chart, error) {
124242
repoName := strings.Split(chartRef, "/")[0]

internal/helm/upgrade.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,18 @@ func HelmUpgrade(
227227
}
228228

229229
// loadChart resolves both local and repo-based charts
230+
// loadChart resolves both local, repo-based, and OCI charts
230231
func loadChart(chartRef, repoURL, version string, debug bool) (*chart.Chart, error) {
231232
if debug {
232233
pterm.Printf("Resolving chart: %s\n", chartRef)
233234
}
234235

236+
// Check for OCI registry reference FIRST
237+
if strings.HasPrefix(chartRef, "oci://") {
238+
fmt.Printf("🐳 Loading OCI chart from registry...\n")
239+
return LoadOCIChart(chartRef, version, cli.New(), debug)
240+
}
241+
235242
// Local path (./chart or /path/to/chart)
236243
if strings.HasPrefix(chartRef, "./") || strings.HasPrefix(chartRef, "/") {
237244
absPath, err := filepath.Abs(chartRef)
@@ -1049,14 +1056,6 @@ func getPodLogs(clientset *kubernetes.Clientset, namespace, podName, containerNa
10491056
return buf.String(), nil
10501057
}
10511058

1052-
// Helper function for max
1053-
func max(a, b int) int {
1054-
if a > b {
1055-
return a
1056-
}
1057-
return b
1058-
}
1059-
10601059
// Print status summary
10611060
func printStatusSummary(statusCount map[string]int, total int) {
10621061
fmt.Printf("📈 Status: ")

0 commit comments

Comments
 (0)