Skip to content

Commit 006857a

Browse files
authored
chore: refactor carvel to use proxied k8s api endpoints (#360)
1 parent 92ad790 commit 006857a

File tree

9 files changed

+902
-3222
lines changed

9 files changed

+902
-3222
lines changed

cli/pkg/server/carvel_diagram.go

Lines changed: 0 additions & 1478 deletions
This file was deleted.

cli/pkg/server/carvel_operations.go

Lines changed: 0 additions & 465 deletions
This file was deleted.

cli/pkg/server/carvel_values.go

Lines changed: 0 additions & 742 deletions
This file was deleted.

cli/pkg/server/k8s_proxy.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@ import (
1919
"github.com/gimlet-io/capacitor/pkg/kubernetes"
2020
"github.com/labstack/echo/v4"
2121
"k8s.io/client-go/discovery"
22-
"k8s.io/client-go/dynamic"
2322
"k8s.io/client-go/rest"
2423
)
2524

2625
// KubernetesProxy handles proxying requests to the Kubernetes API
2726
type KubernetesProxy struct {
2827
k8sClient *kubernetes.Client
29-
dynamicClient dynamic.Interface // Dynamic client for Carvel and other custom resources
3028
proxy *httputil.ReverseProxy
3129
fluxAPIPaths map[string]string // Cache for discovered Flux API paths (kind -> API path template)
3230
fluxAPIPathsMu sync.RWMutex // Mutex for thread-safe access to fluxAPIPaths
@@ -131,17 +129,10 @@ func NewKubernetesProxy(k8sClient *kubernetes.Client) (*KubernetesProxy, error)
131129
io.WriteString(w, fmt.Sprintf("Error proxying request to Kubernetes API: %v", err))
132130
}
133131

134-
// Create dynamic client for Carvel and other custom resources
135-
dynamicClient, err := dynamic.NewForConfig(k8sClient.Config)
136-
if err != nil {
137-
return nil, fmt.Errorf("error creating dynamic client: %w", err)
138-
}
139-
140132
return &KubernetesProxy{
141-
k8sClient: k8sClient,
142-
dynamicClient: dynamicClient,
143-
proxy: proxy,
144-
fluxAPIPaths: make(map[string]string),
133+
k8sClient: k8sClient,
134+
proxy: proxy,
135+
fluxAPIPaths: make(map[string]string),
145136
}, nil
146137
}
147138

cli/pkg/server/server.go

Lines changed: 0 additions & 287 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,166 +1324,6 @@ func (s *Server) Setup() {
13241324
})
13251325
})
13261326

1327-
// Add endpoint for fetching resources from remote clusters (for Carvel apps/pkgis)
1328-
s.echo.GET("/api/:context/carvel-remote-resource", func(c echo.Context) error {
1329-
proxy, ok := getProxyFromContext(c)
1330-
if !ok {
1331-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1332-
}
1333-
1334-
// Get resource details from query params
1335-
namespace := c.QueryParam("namespace")
1336-
kind := c.QueryParam("kind")
1337-
name := c.QueryParam("name")
1338-
apiVersion := c.QueryParam("apiVersion")
1339-
parentKind := c.QueryParam("parentKind")
1340-
parentName := c.QueryParam("parentName")
1341-
parentNamespace := c.QueryParam("parentNamespace")
1342-
1343-
if kind == "" || name == "" || parentKind == "" || parentName == "" || parentNamespace == "" {
1344-
return c.JSON(http.StatusBadRequest, map[string]string{
1345-
"error": "kind, name, parentKind, parentName, and parentNamespace are required",
1346-
})
1347-
}
1348-
1349-
log.Printf("[RemoteResource] Fetching %s/%s from namespace %s via parent %s/%s in namespace %s",
1350-
kind, name, namespace, parentKind, parentName, parentNamespace)
1351-
1352-
// Fetch the parent resource (App or PackageInstall) to get cluster config
1353-
ctx := c.Request().Context()
1354-
var parentGVR schema.GroupVersionResource
1355-
if parentKind == "App" {
1356-
parentGVR = schema.GroupVersionResource{Group: "kappctrl.k14s.io", Version: "v1alpha1", Resource: "apps"}
1357-
} else if parentKind == "PackageInstall" {
1358-
parentGVR = schema.GroupVersionResource{Group: "packaging.carvel.dev", Version: "v1alpha1", Resource: "packageinstalls"}
1359-
} else {
1360-
return c.JSON(http.StatusBadRequest, map[string]string{
1361-
"error": fmt.Sprintf("unsupported parent kind: %s", parentKind),
1362-
})
1363-
}
1364-
1365-
parentResource, err := proxy.dynamicClient.Resource(parentGVR).Namespace(parentNamespace).Get(ctx, parentName, metav1.GetOptions{})
1366-
if err != nil {
1367-
return c.JSON(http.StatusInternalServerError, map[string]string{
1368-
"error": fmt.Sprintf("failed to get parent resource: %v", err),
1369-
})
1370-
}
1371-
1372-
// Create clients for the remote cluster
1373-
remoteClients, clusterName, err := createClientsForCluster(ctx, parentResource.Object, parentNamespace, proxy.k8sClient.Clientset)
1374-
if err != nil {
1375-
return c.JSON(http.StatusInternalServerError, map[string]string{
1376-
"error": fmt.Sprintf("failed to create remote cluster clients: %v", err),
1377-
})
1378-
}
1379-
if remoteClients == nil {
1380-
return c.JSON(http.StatusBadRequest, map[string]string{
1381-
"error": "parent resource does not have spec.cluster.kubeconfigSecretRef configured",
1382-
})
1383-
}
1384-
1385-
log.Printf("[RemoteResource] Using remote cluster: %s", clusterName)
1386-
1387-
// Determine the GVR for the resource
1388-
group := ""
1389-
version := "v1"
1390-
if apiVersion != "" {
1391-
parts := strings.Split(apiVersion, "/")
1392-
if len(parts) == 2 {
1393-
group = parts[0]
1394-
version = parts[1]
1395-
} else {
1396-
version = apiVersion
1397-
}
1398-
}
1399-
1400-
// Get resource plural name and scope from API resources discovery
1401-
discoveryClient := remoteClients.Clientset.Discovery()
1402-
resourceLists, err := discoveryClient.ServerPreferredResources()
1403-
if err != nil {
1404-
log.Printf("[RemoteResource] Warning: failed to get preferred resources: %v", err)
1405-
}
1406-
1407-
resourceName := strings.ToLower(kind) + "s" // default plural
1408-
isNamespaced := true // default to namespaced
1409-
if resourceLists != nil {
1410-
for _, resourceList := range resourceLists {
1411-
gv := strings.Split(resourceList.GroupVersion, "/")
1412-
listGroup := ""
1413-
listVersion := ""
1414-
if len(gv) == 2 {
1415-
listGroup = gv[0]
1416-
listVersion = gv[1]
1417-
} else {
1418-
listVersion = gv[0]
1419-
}
1420-
1421-
if (group == "" && listGroup == "") || group == listGroup {
1422-
if version == listVersion {
1423-
for _, resource := range resourceList.APIResources {
1424-
if strings.EqualFold(resource.Kind, kind) {
1425-
resourceName = resource.Name
1426-
isNamespaced = resource.Namespaced
1427-
log.Printf("[RemoteResource] Found resource: %s (namespaced: %v)", resourceName, isNamespaced)
1428-
break
1429-
}
1430-
}
1431-
}
1432-
}
1433-
}
1434-
}
1435-
1436-
targetGVR := schema.GroupVersionResource{
1437-
Group: group,
1438-
Version: version,
1439-
Resource: resourceName,
1440-
}
1441-
1442-
// Check if managedFields should be included
1443-
includeManagedFields := c.QueryParam("includeManagedFields") == "true"
1444-
1445-
log.Printf("[RemoteResource] Fetching resource with GVR: %s/%s/%s (namespaced: %v, namespace: %s, includeManagedFields: %v)",
1446-
targetGVR.Group, targetGVR.Version, targetGVR.Resource, isNamespaced, namespace, includeManagedFields)
1447-
1448-
// Fetch the resource from remote cluster
1449-
var resource map[string]interface{}
1450-
var fetchErr error
1451-
if isNamespaced {
1452-
// For namespaced resources, use the namespace (or default to "default" if empty)
1453-
ns := namespace
1454-
if ns == "" {
1455-
ns = "default"
1456-
}
1457-
unstructured, err := remoteClients.DynamicClient.Resource(targetGVR).Namespace(ns).Get(ctx, name, metav1.GetOptions{})
1458-
if err == nil {
1459-
resource = unstructured.Object
1460-
}
1461-
fetchErr = err
1462-
} else {
1463-
// For cluster-scoped resources, don't use namespace
1464-
unstructured, err := remoteClients.DynamicClient.Resource(targetGVR).Get(ctx, name, metav1.GetOptions{})
1465-
if err == nil {
1466-
resource = unstructured.Object
1467-
}
1468-
fetchErr = err
1469-
}
1470-
1471-
if fetchErr != nil {
1472-
return c.JSON(http.StatusNotFound, map[string]string{
1473-
"error": fmt.Sprintf("failed to fetch resource from remote cluster: %v", fetchErr),
1474-
})
1475-
}
1476-
1477-
// Remove managedFields if not requested
1478-
if !includeManagedFields {
1479-
if metadata, ok := resource["metadata"].(map[string]interface{}); ok {
1480-
delete(metadata, "managedFields")
1481-
}
1482-
}
1483-
1484-
return c.JSON(http.StatusOK, resource)
1485-
})
1486-
14871327
// Add endpoint for describing Kubernetes resources using kubectl describe (context-aware)
14881328
s.echo.GET("/api/:context/describe/:namespace/:kind/:name", func(c echo.Context) error {
14891329
proxy, ok := getProxyFromContext(c)
@@ -1706,133 +1546,6 @@ func (s *Server) Setup() {
17061546
})
17071547
})
17081548

1709-
// Add endpoint for Carvel App diagram (context-aware)
1710-
s.echo.GET("/api/:context/carvelAppDiagram/:namespace/:name", func(c echo.Context) error {
1711-
proxy, ok := getProxyFromContext(c)
1712-
if !ok {
1713-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1714-
}
1715-
return handleCarvelAppDiagram(c, proxy)
1716-
})
1717-
1718-
// Add endpoint for Carvel PackageInstall diagram (context-aware)
1719-
s.echo.GET("/api/:context/carvelPkgiDiagram/:namespace/:name", func(c echo.Context) error {
1720-
proxy, ok := getProxyFromContext(c)
1721-
if !ok {
1722-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1723-
}
1724-
return handleCarvelPkgiDiagram(c, proxy)
1725-
})
1726-
1727-
// Add endpoint for Carvel App values and overlays (context-aware)
1728-
s.echo.GET("/api/:context/carvelAppValues/:namespace/:name", func(c echo.Context) error {
1729-
proxy, ok := getProxyFromContext(c)
1730-
if !ok {
1731-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1732-
}
1733-
return handleCarvelAppValues(c, proxy)
1734-
})
1735-
1736-
// Add endpoint for Carvel PackageInstall values and overlays (context-aware)
1737-
s.echo.GET("/api/:context/carvelPackageInstallValues/:namespace/:name", func(c echo.Context) error {
1738-
proxy, ok := getProxyFromContext(c)
1739-
if !ok {
1740-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1741-
}
1742-
return handleCarvelPackageInstallValues(c, proxy)
1743-
})
1744-
1745-
// Add endpoint for Carvel Package details (context-aware)
1746-
s.echo.GET("/api/:context/carvelPackage/:namespace/:packageName", func(c echo.Context) error {
1747-
proxy, ok := getProxyFromContext(c)
1748-
if !ok {
1749-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1750-
}
1751-
return handleCarvelPackage(c, proxy)
1752-
})
1753-
1754-
// Add endpoints for Carvel App operations (context-aware)
1755-
s.echo.POST("/api/:context/carvelApp/:namespace/:name/pause", func(c echo.Context) error {
1756-
proxy, ok := getProxyFromContext(c)
1757-
if !ok {
1758-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1759-
}
1760-
return handleCarvelAppPause(c, proxy)
1761-
})
1762-
1763-
s.echo.POST("/api/:context/carvelApp/:namespace/:name/unpause", func(c echo.Context) error {
1764-
proxy, ok := getProxyFromContext(c)
1765-
if !ok {
1766-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1767-
}
1768-
return handleCarvelAppUnpause(c, proxy)
1769-
})
1770-
1771-
s.echo.POST("/api/:context/carvelApp/:namespace/:name/cancel", func(c echo.Context) error {
1772-
proxy, ok := getProxyFromContext(c)
1773-
if !ok {
1774-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1775-
}
1776-
return handleCarvelAppCancel(c, proxy)
1777-
})
1778-
1779-
s.echo.POST("/api/:context/carvelApp/:namespace/:name/uncancel", func(c echo.Context) error {
1780-
proxy, ok := getProxyFromContext(c)
1781-
if !ok {
1782-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1783-
}
1784-
return handleCarvelAppUncancel(c, proxy)
1785-
})
1786-
1787-
s.echo.POST("/api/:context/carvelApp/:namespace/:name/trigger", func(c echo.Context) error {
1788-
proxy, ok := getProxyFromContext(c)
1789-
if !ok {
1790-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1791-
}
1792-
return handleCarvelAppTrigger(c, proxy)
1793-
})
1794-
1795-
// Add endpoints for Carvel PackageInstall operations (context-aware)
1796-
s.echo.POST("/api/:context/carvelPackageInstall/:namespace/:name/pause", func(c echo.Context) error {
1797-
proxy, ok := getProxyFromContext(c)
1798-
if !ok {
1799-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1800-
}
1801-
return handleCarvelPackageInstallPause(c, proxy)
1802-
})
1803-
1804-
s.echo.POST("/api/:context/carvelPackageInstall/:namespace/:name/unpause", func(c echo.Context) error {
1805-
proxy, ok := getProxyFromContext(c)
1806-
if !ok {
1807-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1808-
}
1809-
return handleCarvelPackageInstallUnpause(c, proxy)
1810-
})
1811-
1812-
s.echo.POST("/api/:context/carvelPackageInstall/:namespace/:name/cancel", func(c echo.Context) error {
1813-
proxy, ok := getProxyFromContext(c)
1814-
if !ok {
1815-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1816-
}
1817-
return handleCarvelPackageInstallCancel(c, proxy)
1818-
})
1819-
1820-
s.echo.POST("/api/:context/carvelPackageInstall/:namespace/:name/uncancel", func(c echo.Context) error {
1821-
proxy, ok := getProxyFromContext(c)
1822-
if !ok {
1823-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1824-
}
1825-
return handleCarvelPackageInstallUncancel(c, proxy)
1826-
})
1827-
1828-
s.echo.POST("/api/:context/carvelPackageInstall/:namespace/:name/trigger", func(c echo.Context) error {
1829-
proxy, ok := getProxyFromContext(c)
1830-
if !ok {
1831-
return c.JSON(http.StatusBadRequest, map[string]string{"error": "missing proxy in context"})
1832-
}
1833-
return handleCarvelPackageInstallTrigger(c, proxy)
1834-
})
1835-
18361549
// Kubernetes API proxy endpoints
18371550
// New: match routes with explicit context: /k8s/:context/*
18381551
s.echo.Any("/k8s/:context/*", func(c echo.Context) error {

0 commit comments

Comments
 (0)