Skip to content

Commit 671fab3

Browse files
committed
fix: HR with OCIRepository reconcile with sources
1 parent 2387a40 commit 671fab3

File tree

1 file changed

+96
-83
lines changed

1 file changed

+96
-83
lines changed

cli/pkg/server/server.go

Lines changed: 96 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ type CarvelResponse struct {
107107
type ConfigResponse struct {
108108
SystemViews map[string][]SystemView `json:"systemViews"`
109109
FluxCD FluxCDResponse `json:"fluxcd"`
110-
Carvel CarvelResponse `json:"carvel"`
111-
}
110+
Carvel CarvelResponse `json:"carvel"`
111+
}
112112

113113
// defaultSystemViews contains the built‑in system views that were previously hardcoded in ViewBar.tsx.
114114
// They are now served from the backend so they can be centrally controlled and customized.
@@ -176,7 +176,7 @@ var defaultSystemViews = []SystemView{
176176
{Name: "Namespace", Value: "all-namespaces"},
177177
},
178178
},
179-
}
179+
}
180180

181181
// defaultSystemViewMap exposes the built‑in system views under the "*"
182182
// wildcard key so that all contexts share the same defaults unless
@@ -482,7 +482,8 @@ func (s *Server) Setup() {
482482
})
483483
}
484484

485-
// HelmRelease has sourceRef at spec.chart.spec.sourceRef or via spec.chartRef (HelmChart resource),
485+
// HelmRelease has sourceRef at spec.chart.spec.sourceRef or via spec.chartRef
486+
// (either a HelmChart resource which in turn has spec.sourceRef, or directly an OCIRepository).
486487
// while Kustomization and Terraform have it at spec.sourceRef
487488
var sourceRef map[string]interface{}
488489
if kind == "HelmRelease" {
@@ -495,7 +496,7 @@ func (s *Server) Setup() {
495496
}
496497
}
497498

498-
// If no inline chart sourceRef, try chartRef (HelmChart resource)
499+
// If no inline chart sourceRef, try chartRef (HelmChart or OCIRepository resource)
499500
if sourceRef == nil {
500501
if chartRef, ok := spec["chartRef"].(map[string]interface{}); ok {
501502
chartRefKind, _ := chartRef["kind"].(string)
@@ -505,37 +506,50 @@ func (s *Server) Setup() {
505506
chartRefNamespace = ns
506507
}
507508

508-
// Get the HelmChart resource to find its sourceRef
509-
if chartRefKind == "HelmChart" && chartRefName != "" {
510-
helmChartAPIPath, err := proxy.getFluxAPIPath(ctx, "HelmChart")
511-
if err != nil {
512-
log.Printf("Error discovering HelmChart API path: %v", err)
513-
return c.JSON(http.StatusInternalServerError, map[string]string{
514-
"error": fmt.Sprintf("Failed to discover HelmChart API path: %v", err),
515-
})
516-
}
509+
if chartRefName != "" {
510+
switch chartRefKind {
511+
case "HelmChart":
512+
// Get the HelmChart resource to find its sourceRef
513+
helmChartAPIPath, err := proxy.getFluxAPIPath(ctx, "HelmChart")
514+
if err != nil {
515+
log.Printf("Error discovering HelmChart API path: %v", err)
516+
return c.JSON(http.StatusInternalServerError, map[string]string{
517+
"error": fmt.Sprintf("Failed to discover HelmChart API path: %v", err),
518+
})
519+
}
517520

518-
helmChartPath := fmt.Sprintf(helmChartAPIPath, chartRefNamespace, chartRefName)
519-
helmChartData, err := clientset.RESTClient().Get().AbsPath(helmChartPath).DoRaw(ctx)
520-
if err != nil {
521-
log.Printf("Error getting HelmChart resource: %v", err)
522-
return c.JSON(http.StatusInternalServerError, map[string]string{
523-
"error": fmt.Sprintf("Failed to get HelmChart resource: %v", err),
524-
})
525-
}
521+
helmChartPath := fmt.Sprintf(helmChartAPIPath, chartRefNamespace, chartRefName)
522+
helmChartData, err := clientset.RESTClient().Get().AbsPath(helmChartPath).DoRaw(ctx)
523+
if err != nil {
524+
log.Printf("Error getting HelmChart resource: %v", err)
525+
return c.JSON(http.StatusInternalServerError, map[string]string{
526+
"error": fmt.Sprintf("Failed to get HelmChart resource: %v", err),
527+
})
528+
}
526529

527-
var helmChartObj map[string]interface{}
528-
if err := json.Unmarshal(helmChartData, &helmChartObj); err != nil {
529-
log.Printf("Error parsing HelmChart data: %v", err)
530-
return c.JSON(http.StatusInternalServerError, map[string]string{
531-
"error": fmt.Sprintf("Failed to parse HelmChart data: %v", err),
532-
})
533-
}
530+
var helmChartObj map[string]interface{}
531+
if err := json.Unmarshal(helmChartData, &helmChartObj); err != nil {
532+
log.Printf("Error parsing HelmChart data: %v", err)
533+
return c.JSON(http.StatusInternalServerError, map[string]string{
534+
"error": fmt.Sprintf("Failed to parse HelmChart data: %v", err),
535+
})
536+
}
534537

535-
if helmChartSpec, ok := helmChartObj["spec"].(map[string]interface{}); ok {
536-
if srcRef, ok := helmChartSpec["sourceRef"].(map[string]interface{}); ok {
537-
sourceRef = srcRef
538+
if helmChartSpec, ok := helmChartObj["spec"].(map[string]interface{}); ok {
539+
if srcRef, ok := helmChartSpec["sourceRef"].(map[string]interface{}); ok {
540+
sourceRef = srcRef
541+
}
538542
}
543+
case "OCIRepository":
544+
// When chartRef points directly at an OCIRepository, the repository itself is the source.
545+
srcRef := map[string]interface{}{
546+
"kind": "OCIRepository",
547+
"name": chartRefName,
548+
}
549+
if chartRefNamespace != "" {
550+
srcRef["namespace"] = chartRefNamespace
551+
}
552+
sourceRef = srcRef
539553
}
540554
}
541555
}
@@ -1243,7 +1257,6 @@ func (s *Server) Setup() {
12431257
})
12441258
})
12451259

1246-
12471260
// Add endpoint for running a CronJob immediately by creating a one-off Job (context-aware)
12481261
// Equivalent to: kubectl create job --from=cronjob/<name> <generated-name>
12491262
s.echo.POST("/api/:context/cronjob/run", func(c echo.Context) error {
@@ -1333,7 +1346,7 @@ func (s *Server) Setup() {
13331346
})
13341347
}
13351348

1336-
log.Printf("[RemoteResource] Fetching %s/%s from namespace %s via parent %s/%s in namespace %s",
1349+
log.Printf("[RemoteResource] Fetching %s/%s from namespace %s via parent %s/%s in namespace %s",
13371350
kind, name, namespace, parentKind, parentName, parentNamespace)
13381351

13391352
// Fetch the parent resource (App or PackageInstall) to get cluster config
@@ -1392,7 +1405,7 @@ func (s *Server) Setup() {
13921405
}
13931406

13941407
resourceName := strings.ToLower(kind) + "s" // default plural
1395-
isNamespaced := true // default to namespaced
1408+
isNamespaced := true // default to namespaced
13961409
if resourceLists != nil {
13971410
for _, resourceList := range resourceLists {
13981411
gv := strings.Split(resourceList.GroupVersion, "/")
@@ -1428,8 +1441,8 @@ func (s *Server) Setup() {
14281441

14291442
// Check if managedFields should be included
14301443
includeManagedFields := c.QueryParam("includeManagedFields") == "true"
1431-
1432-
log.Printf("[RemoteResource] Fetching resource with GVR: %s/%s/%s (namespaced: %v, namespace: %s, includeManagedFields: %v)",
1444+
1445+
log.Printf("[RemoteResource] Fetching resource with GVR: %s/%s/%s (namespaced: %v, namespace: %s, includeManagedFields: %v)",
14331446
targetGVR.Group, targetGVR.Version, targetGVR.Resource, isNamespaced, namespace, includeManagedFields)
14341447

14351448
// Fetch the resource from remote cluster
@@ -2697,80 +2710,80 @@ func (s *Server) handleExecWebSocketWithClient(c echo.Context, client *kubernete
26972710

26982711
// Try each container and shell combination until one works
26992712
for _, tryContainer := range containersToTry {
2700-
for _, tryShell := range shells {
2713+
for _, tryShell := range shells {
27012714
log.Printf("Trying shell %s in container %s for pod %s/%s", tryShell, tryContainer, namespace, podname)
27022715

2703-
var shellExists bool
2704-
testMethods := [][]string{
2705-
{tryShell, "--version"},
2706-
{tryShell, "--help"},
2707-
{tryShell, "-c", "exit 0"},
2708-
}
2716+
var shellExists bool
2717+
testMethods := [][]string{
2718+
{tryShell, "--version"},
2719+
{tryShell, "--help"},
2720+
{tryShell, "-c", "exit 0"},
2721+
}
27092722

2710-
for _, testCmd := range testMethods {
2711-
testReq := client.Clientset.CoreV1().RESTClient().Post().
2712-
Resource("pods").
2713-
Name(podname).
2714-
Namespace(namespace).
2715-
SubResource("exec").
2716-
VersionedParams(&corev1.PodExecOptions{
2723+
for _, testCmd := range testMethods {
2724+
testReq := client.Clientset.CoreV1().RESTClient().Post().
2725+
Resource("pods").
2726+
Name(podname).
2727+
Namespace(namespace).
2728+
SubResource("exec").
2729+
VersionedParams(&corev1.PodExecOptions{
27172730
Container: tryContainer,
27182731
Command: testCmd,
27192732
Stdin: false,
27202733
Stdout: true,
27212734
Stderr: true,
27222735
TTY: false,
2723-
}, scheme.ParameterCodec)
2736+
}, scheme.ParameterCodec)
27242737

2725-
testExec, err := remotecommand.NewSPDYExecutor(client.Config, "POST", testReq.URL())
2726-
if err != nil {
2727-
continue
2728-
}
2738+
testExec, err := remotecommand.NewSPDYExecutor(client.Config, "POST", testReq.URL())
2739+
if err != nil {
2740+
continue
2741+
}
27292742

2730-
var testOut, testErr bytes.Buffer
2743+
var testOut, testErr bytes.Buffer
27312744
err = testExec.StreamWithContext(ctx, remotecommand.StreamOptions{
2732-
Stdout: &testOut,
2733-
Stderr: &testErr,
2734-
})
2745+
Stdout: &testOut,
2746+
Stderr: &testErr,
2747+
})
27352748

2736-
if err == nil {
2737-
shellExists = true
2749+
if err == nil {
2750+
shellExists = true
27382751
log.Printf("Shell %s found in container %s using test: %v", tryShell, tryContainer, testCmd)
2739-
break
2752+
break
2753+
}
27402754
}
2741-
}
27422755

2743-
if !shellExists {
2756+
if !shellExists {
27442757
log.Printf("Shell %s not found in container %s", tryShell, tryContainer)
2745-
continue
2746-
}
2758+
continue
2759+
}
27472760

2748-
execCmd = []string{tryShell}
2749-
req := client.Clientset.CoreV1().RESTClient().Post().
2750-
Resource("pods").
2751-
Name(podname).
2752-
Namespace(namespace).
2753-
SubResource("exec").
2754-
VersionedParams(&corev1.PodExecOptions{
2761+
execCmd = []string{tryShell}
2762+
req := client.Clientset.CoreV1().RESTClient().Post().
2763+
Resource("pods").
2764+
Name(podname).
2765+
Namespace(namespace).
2766+
SubResource("exec").
2767+
VersionedParams(&corev1.PodExecOptions{
27552768
Container: tryContainer,
27562769
Command: execCmd,
27572770
Stdin: true,
27582771
Stdout: true,
27592772
Stderr: true,
27602773
TTY: true,
2761-
}, scheme.ParameterCodec)
2774+
}, scheme.ParameterCodec)
27622775

2763-
exec, err := remotecommand.NewSPDYExecutor(client.Config, "POST", req.URL())
2764-
if err != nil {
2776+
exec, err := remotecommand.NewSPDYExecutor(client.Config, "POST", req.URL())
2777+
if err != nil {
27652778
log.Printf("Failed to create interactive executor for shell %s in container %s: %v", tryShell, tryContainer, err)
2766-
continue
2767-
}
2779+
continue
2780+
}
27682781

2769-
executor = exec
2770-
shell = tryShell
2782+
executor = exec
2783+
shell = tryShell
27712784
containerName = tryContainer
2772-
shellFound = true
2773-
break
2785+
shellFound = true
2786+
break
27742787
}
27752788

27762789
if shellFound {

0 commit comments

Comments
 (0)