diff --git a/internal/api/apis/apps/deployments/create.go b/internal/api/apis/apps/deployments/create.go index 9162421..ffa42e7 100644 --- a/internal/api/apis/apps/deployments/create.go +++ b/internal/api/apis/apps/deployments/create.go @@ -13,10 +13,9 @@ import ( ) func (svc DeploymentService) CreateDeployment(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) deployment := &appsv1.Deployment{} - err := httputils.ParseJSONBody(r.Request, &deployment) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/apis/apps/deployments/delete.go b/internal/api/apis/apps/deployments/delete.go index 19cb489..1530d32 100644 --- a/internal/api/apis/apps/deployments/delete.go +++ b/internal/api/apis/apps/deployments/delete.go @@ -4,13 +4,14 @@ import ( "net/http" "github.com/emicklei/go-restful/v3" + "github.com/portainer/k2d/internal/api/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func (svc DeploymentService) DeleteDeployment(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - deploymentName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + deploymentName := r.PathParameter("name") svc.adapter.DeleteContainer(r.Request.Context(), deploymentName, namespace) w.WriteAsJson(metav1.Status{ diff --git a/internal/api/apis/apps/deployments/deployments.go b/internal/api/apis/apps/deployments/deployments.go index 412906f..ea9834d 100644 --- a/internal/api/apis/apps/deployments/deployments.go +++ b/internal/api/apis/apps/deployments/deployments.go @@ -3,6 +3,7 @@ package deployments import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" "github.com/portainer/k2d/internal/controller" ) @@ -30,6 +31,7 @@ func (svc DeploymentService) RegisterDeploymentAPI(ws *restful.WebService) { Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) ws.Route(ws.POST("/v1/namespaces/{namespace}/deployments"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.CreateDeployment). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) @@ -38,6 +40,7 @@ func (svc DeploymentService) RegisterDeploymentAPI(ws *restful.WebService) { To(svc.ListDeployments)) ws.Route(ws.GET("/v1/namespaces/{namespace}/deployments"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.ListDeployments). Param(ws.PathParameter("namespace", "namespace name").DataType("string"))) @@ -46,6 +49,7 @@ func (svc DeploymentService) RegisterDeploymentAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the deployment").DataType("string"))) ws.Route(ws.DELETE("/v1/namespaces/{namespace}/deployments/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.DeleteDeployment). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the deployment").DataType("string"))) @@ -55,6 +59,7 @@ func (svc DeploymentService) RegisterDeploymentAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the deployment").DataType("string"))) ws.Route(ws.GET("/v1/namespaces/{namespace}/deployments/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetDeployment). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the deployment").DataType("string"))) @@ -66,6 +71,7 @@ func (svc DeploymentService) RegisterDeploymentAPI(ws *restful.WebService) { AddExtension("x-kubernetes-group-version-kind", deploymentGVKExtension)) ws.Route(ws.PATCH("/v1/namespaces/{namespace}/deployments/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PatchDeployment). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the deployment").DataType("string")). diff --git a/internal/api/apis/apps/deployments/get.go b/internal/api/apis/apps/deployments/get.go index aa9164b..322f846 100644 --- a/internal/api/apis/apps/deployments/get.go +++ b/internal/api/apis/apps/deployments/get.go @@ -11,7 +11,7 @@ import ( ) func (svc DeploymentService) GetDeployment(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) deploymentName := r.PathParameter("name") deployment, err := svc.adapter.GetDeployment(r.Request.Context(), deploymentName, namespace) diff --git a/internal/api/apis/apps/deployments/list.go b/internal/api/apis/apps/deployments/list.go index ac80f57..150632a 100644 --- a/internal/api/apis/apps/deployments/list.go +++ b/internal/api/apis/apps/deployments/list.go @@ -9,7 +9,7 @@ import ( ) func (svc DeploymentService) ListDeployments(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) utils.ListResources( r, diff --git a/internal/api/apis/apps/deployments/patch.go b/internal/api/apis/apps/deployments/patch.go index 5a4e70f..a12f322 100644 --- a/internal/api/apis/apps/deployments/patch.go +++ b/internal/api/apis/apps/deployments/patch.go @@ -15,9 +15,9 @@ import ( ) func (svc DeploymentService) PatchDeployment(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - deploymentName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + deploymentName := r.PathParameter("name") patch, err := io.ReadAll(r.Request.Body) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/configmaps/configmaps.go b/internal/api/core/v1/configmaps/configmaps.go index a3632a3..f7c032d 100644 --- a/internal/api/core/v1/configmaps/configmaps.go +++ b/internal/api/core/v1/configmaps/configmaps.go @@ -3,6 +3,7 @@ package configmaps import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" "github.com/portainer/k2d/internal/controller" ) @@ -30,6 +31,7 @@ func (svc ConfigMapService) RegisterConfigMapAPI(ws *restful.WebService) { Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) ws.Route(ws.POST("/v1/namespaces/{namespace}/configmaps"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.CreateConfigMap). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) @@ -38,6 +40,7 @@ func (svc ConfigMapService) RegisterConfigMapAPI(ws *restful.WebService) { To(svc.ListConfigMaps)) ws.Route(ws.GET("/v1/namespaces/{namespace}/configmaps"). + Filter(utils.NamespaceValidation(svc.adapter)). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). To(svc.ListConfigMaps)) @@ -46,6 +49,7 @@ func (svc ConfigMapService) RegisterConfigMapAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the configmap").DataType("string"))) ws.Route(ws.DELETE("/v1/namespaces/{namespace}/configmaps/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.DeleteConfigMap). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the configmap").DataType("string"))) @@ -55,6 +59,7 @@ func (svc ConfigMapService) RegisterConfigMapAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the configmap").DataType("string"))) ws.Route(ws.GET("/v1/namespaces/{namespace}/configmaps/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetConfigMap). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the configmap").DataType("string"))) @@ -66,6 +71,7 @@ func (svc ConfigMapService) RegisterConfigMapAPI(ws *restful.WebService) { AddExtension("x-kubernetes-group-version-kind", configMapGVKExtension)) ws.Route(ws.PATCH("/v1/namespaces/{namespace}/configmaps/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PatchConfigMap). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the configmap").DataType("string")). diff --git a/internal/api/core/v1/configmaps/create.go b/internal/api/core/v1/configmaps/create.go index dc7dd29..aa18401 100644 --- a/internal/api/core/v1/configmaps/create.go +++ b/internal/api/core/v1/configmaps/create.go @@ -13,10 +13,9 @@ import ( ) func (svc ConfigMapService) CreateConfigMap(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) configMap := &corev1.ConfigMap{} - err := httputils.ParseJSONBody(r.Request, &configMap) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/configmaps/delete.go b/internal/api/core/v1/configmaps/delete.go index a44bcb0..f506517 100644 --- a/internal/api/core/v1/configmaps/delete.go +++ b/internal/api/core/v1/configmaps/delete.go @@ -10,9 +10,9 @@ import ( ) func (svc ConfigMapService) DeleteConfigMap(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - configMapName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + configMapName := r.PathParameter("name") err := svc.adapter.DeleteConfigMap(configMapName, namespace) if err != nil { utils.HttpError(r, w, http.StatusInternalServerError, fmt.Errorf("unable to delete configmap: %w", err)) diff --git a/internal/api/core/v1/configmaps/get.go b/internal/api/core/v1/configmaps/get.go index 08a1564..0e5f619 100644 --- a/internal/api/core/v1/configmaps/get.go +++ b/internal/api/core/v1/configmaps/get.go @@ -11,7 +11,7 @@ import ( ) func (svc ConfigMapService) GetConfigMap(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) configMapName := r.PathParameter("name") configMap, err := svc.adapter.GetConfigMap(configMapName, namespace) diff --git a/internal/api/core/v1/configmaps/list.go b/internal/api/core/v1/configmaps/list.go index ba799d1..45461b2 100644 --- a/internal/api/core/v1/configmaps/list.go +++ b/internal/api/core/v1/configmaps/list.go @@ -9,7 +9,7 @@ import ( ) func (svc ConfigMapService) ListConfigMaps(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) utils.ListResources( r, diff --git a/internal/api/core/v1/configmaps/patch.go b/internal/api/core/v1/configmaps/patch.go index ce50a05..6f8ed4e 100644 --- a/internal/api/core/v1/configmaps/patch.go +++ b/internal/api/core/v1/configmaps/patch.go @@ -17,9 +17,9 @@ import ( ) func (svc ConfigMapService) PatchConfigMap(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - configMapName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + configMapName := r.PathParameter("name") patch, err := io.ReadAll(r.Request.Body) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/events/events.go b/internal/api/core/v1/events/events.go index 61f2712..819e0cc 100644 --- a/internal/api/core/v1/events/events.go +++ b/internal/api/core/v1/events/events.go @@ -3,6 +3,7 @@ package events import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" ) type EventService struct { @@ -20,5 +21,6 @@ func (svc EventService) RegisterEventAPI(ws *restful.WebService) { To(svc.ListEvents)) ws.Route(ws.GET("/v1/namespaces/{namespace}/events"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.ListEvents)) } diff --git a/internal/api/core/v1/namespaces/delete.go b/internal/api/core/v1/namespaces/delete.go index 5500dc8..03dd22f 100644 --- a/internal/api/core/v1/namespaces/delete.go +++ b/internal/api/core/v1/namespaces/delete.go @@ -10,7 +10,7 @@ import ( ) func (svc NamespaceService) DeleteNamespace(r *restful.Request, w *restful.Response) { - namespaceName := r.PathParameter("name") + namespaceName := utils.GetNamespaceFromRequest(r) err := svc.adapter.DeleteNamespace(r.Request.Context(), namespaceName) if err != nil { diff --git a/internal/api/core/v1/namespaces/get.go b/internal/api/core/v1/namespaces/get.go index ae0bc5a..062248b 100644 --- a/internal/api/core/v1/namespaces/get.go +++ b/internal/api/core/v1/namespaces/get.go @@ -11,9 +11,9 @@ import ( ) func (svc NamespaceService) GetNamespace(r *restful.Request, w *restful.Response) { - name := r.PathParameter("name") + namespaceName := utils.GetNamespaceFromRequest(r) - namespace, err := svc.adapter.GetNamespace(r.Request.Context(), name) + namespace, err := svc.adapter.GetNamespace(r.Request.Context(), namespaceName) if err != nil { if errors.Is(err, adaptererr.ErrResourceNotFound) { w.WriteHeader(http.StatusNotFound) diff --git a/internal/api/core/v1/namespaces/namespaces.go b/internal/api/core/v1/namespaces/namespaces.go index e718cda..7b1675f 100644 --- a/internal/api/core/v1/namespaces/namespaces.go +++ b/internal/api/core/v1/namespaces/namespaces.go @@ -3,6 +3,7 @@ package namespaces import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" "github.com/portainer/k2d/internal/controller" ) @@ -32,17 +33,20 @@ func (svc NamespaceService) RegisterNamespaceAPI(ws *restful.WebService) { ws.Route(ws.GET("/v1/namespaces"). To(svc.ListNamespaces)) - ws.Route(ws.GET("/v1/namespaces/{name}"). + ws.Route(ws.GET("/v1/namespaces/{namespace}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetNamespace). - Param(ws.PathParameter("name", "name of the namespace").DataType("string"))) + Param(ws.PathParameter("namespace", "name of the namespace").DataType("string"))) - ws.Route(ws.PATCH("/v1/namespaces/{name}"). + ws.Route(ws.PATCH("/v1/namespaces/{namespace}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PatchNamespace). - Param(ws.PathParameter("name", "name of the namespace").DataType("string")). + Param(ws.PathParameter("namespace", "name of the namespace").DataType("string")). Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string")). AddExtension("x-kubernetes-group-version-kind", namespaceGVKExtension)) - ws.Route(ws.DELETE("/v1/namespaces/{name}"). + ws.Route(ws.DELETE("/v1/namespaces/{namespace}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.DeleteNamespace). - Param(ws.PathParameter("name", "name of the namespace").DataType("string"))) + Param(ws.PathParameter("namespace", "name of the namespace").DataType("string"))) } diff --git a/internal/api/core/v1/namespaces/patch.go b/internal/api/core/v1/namespaces/patch.go index 8451b48..1f2d124 100644 --- a/internal/api/core/v1/namespaces/patch.go +++ b/internal/api/core/v1/namespaces/patch.go @@ -15,7 +15,7 @@ import ( ) func (svc NamespaceService) PatchNamespace(r *restful.Request, w *restful.Response) { - namespaceName := r.PathParameter("name") + namespaceName := utils.GetNamespaceFromRequest(r) patch, err := io.ReadAll(r.Request.Body) if err != nil { diff --git a/internal/api/core/v1/persistentvolumeclaims/create.go b/internal/api/core/v1/persistentvolumeclaims/create.go index 5472add..5da456f 100644 --- a/internal/api/core/v1/persistentvolumeclaims/create.go +++ b/internal/api/core/v1/persistentvolumeclaims/create.go @@ -13,9 +13,9 @@ import ( ) func (svc PersistentVolumeClaimService) CreatePersistentVolumeClaim(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - persistentVolumeClaim := &corev1.PersistentVolumeClaim{} + namespace := utils.GetNamespaceFromRequest(r) + persistentVolumeClaim := &corev1.PersistentVolumeClaim{} err := httputils.ParseJSONBody(r.Request, &persistentVolumeClaim) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/persistentvolumeclaims/delete.go b/internal/api/core/v1/persistentvolumeclaims/delete.go index 1fab0b9..a64b9a4 100644 --- a/internal/api/core/v1/persistentvolumeclaims/delete.go +++ b/internal/api/core/v1/persistentvolumeclaims/delete.go @@ -10,9 +10,9 @@ import ( ) func (svc PersistentVolumeClaimService) DeletePersistentVolumeClaim(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - persistentVolumeClaimName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + persistentVolumeClaimName := r.PathParameter("name") err := svc.adapter.DeletePersistentVolumeClaim(r.Request.Context(), persistentVolumeClaimName, namespace) if err != nil { utils.HttpError(r, w, http.StatusInternalServerError, fmt.Errorf("unable to delete persistent volume claim: %w", err)) diff --git a/internal/api/core/v1/persistentvolumeclaims/get.go b/internal/api/core/v1/persistentvolumeclaims/get.go index 85a3399..7e4cc9e 100644 --- a/internal/api/core/v1/persistentvolumeclaims/get.go +++ b/internal/api/core/v1/persistentvolumeclaims/get.go @@ -11,7 +11,7 @@ import ( ) func (svc PersistentVolumeClaimService) GetPersistentVolumeClaim(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) persistentVolumeClaimName := r.PathParameter("name") persistentVolumeClaim, err := svc.adapter.GetPersistentVolumeClaim(r.Request.Context(), persistentVolumeClaimName, namespace) diff --git a/internal/api/core/v1/persistentvolumeclaims/list.go b/internal/api/core/v1/persistentvolumeclaims/list.go index b4dc458..4b3df0e 100644 --- a/internal/api/core/v1/persistentvolumeclaims/list.go +++ b/internal/api/core/v1/persistentvolumeclaims/list.go @@ -9,7 +9,7 @@ import ( ) func (svc PersistentVolumeClaimService) ListPersistentVolumeClaims(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) utils.ListResources( r, diff --git a/internal/api/core/v1/persistentvolumeclaims/patch.go b/internal/api/core/v1/persistentvolumeclaims/patch.go index 4ad16ab..f13bb44 100644 --- a/internal/api/core/v1/persistentvolumeclaims/patch.go +++ b/internal/api/core/v1/persistentvolumeclaims/patch.go @@ -15,9 +15,9 @@ import ( ) func (svc PersistentVolumeClaimService) PatchPersistentVolumeClaim(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - persistentVolumeClaimName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + persistentVolumeClaimName := r.PathParameter("name") patch, err := io.ReadAll(r.Request.Body) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/persistentvolumeclaims/persistentvolumeclaims.go b/internal/api/core/v1/persistentvolumeclaims/persistentvolumeclaims.go index 2569c33..b75bdb1 100644 --- a/internal/api/core/v1/persistentvolumeclaims/persistentvolumeclaims.go +++ b/internal/api/core/v1/persistentvolumeclaims/persistentvolumeclaims.go @@ -3,6 +3,7 @@ package persistentvolumeclaims import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" "github.com/portainer/k2d/internal/controller" ) @@ -30,6 +31,7 @@ func (svc PersistentVolumeClaimService) RegisterPersistentVolumeClaimAPI(ws *res Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) ws.Route(ws.POST("/v1/namespaces/{namespace}/persistentvolumeclaims"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.CreatePersistentVolumeClaim). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) @@ -37,15 +39,17 @@ func (svc PersistentVolumeClaimService) RegisterPersistentVolumeClaimAPI(ws *res ws.Route(ws.GET("/v1/persistentvolumeclaims"). To(svc.ListPersistentVolumeClaims)) - ws.Route(ws.GET("/v1/persistentvolumeclaims/{name}"). - To(svc.GetPersistentVolumeClaim). - Param(ws.PathParameter("name", "name of the persistentvolumeclaims").DataType("string"))) - ws.Route(ws.GET("/v1/namespaces/{namespace}/persistentvolumeclaims"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.ListPersistentVolumeClaims). Param(ws.PathParameter("namespace", "namespace name").DataType("string"))) + ws.Route(ws.GET("/v1/persistentvolumeclaims/{name}"). + To(svc.GetPersistentVolumeClaim). + Param(ws.PathParameter("name", "name of the persistentvolumeclaims").DataType("string"))) + ws.Route(ws.GET("/v1/namespaces/{namespace}/persistentvolumeclaims/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetPersistentVolumeClaim). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the persistentvolumeclaim").DataType("string"))) @@ -55,6 +59,7 @@ func (svc PersistentVolumeClaimService) RegisterPersistentVolumeClaimAPI(ws *res Param(ws.PathParameter("name", "name of the persistentvolumeclaim").DataType("string"))) ws.Route(ws.DELETE("/v1/namespaces/{namespace}/persistentvolumeclaims/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.DeletePersistentVolumeClaim). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the persistentvolumeclaim").DataType("string"))) @@ -66,6 +71,7 @@ func (svc PersistentVolumeClaimService) RegisterPersistentVolumeClaimAPI(ws *res AddExtension("x-kubernetes-group-version-kind", persistentVolumeClaimGVKExtension)) ws.Route(ws.PATCH("/v1/namespaces/{namespace}/persistentvolumeclaims/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PatchPersistentVolumeClaim). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the persistentvolumeclaim").DataType("string")). diff --git a/internal/api/core/v1/pods/create.go b/internal/api/core/v1/pods/create.go index 0da2b64..3473a85 100644 --- a/internal/api/core/v1/pods/create.go +++ b/internal/api/core/v1/pods/create.go @@ -13,10 +13,9 @@ import ( ) func (svc PodService) CreatePod(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) pod := &corev1.Pod{} - err := httputils.ParseJSONBody(r.Request, &pod) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/pods/delete.go b/internal/api/core/v1/pods/delete.go index 352d8c4..e7d1b31 100644 --- a/internal/api/core/v1/pods/delete.go +++ b/internal/api/core/v1/pods/delete.go @@ -4,13 +4,14 @@ import ( "net/http" "github.com/emicklei/go-restful/v3" + "github.com/portainer/k2d/internal/api/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func (svc PodService) DeletePod(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - podName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + podName := r.PathParameter("name") svc.adapter.DeleteContainer(r.Request.Context(), podName, namespace) w.WriteAsJson(metav1.Status{ diff --git a/internal/api/core/v1/pods/get.go b/internal/api/core/v1/pods/get.go index 3a649fa..b6554ef 100644 --- a/internal/api/core/v1/pods/get.go +++ b/internal/api/core/v1/pods/get.go @@ -11,7 +11,7 @@ import ( ) func (svc PodService) GetPod(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) podName := r.PathParameter("name") pod, err := svc.adapter.GetPod(r.Request.Context(), podName, namespace) diff --git a/internal/api/core/v1/pods/list.go b/internal/api/core/v1/pods/list.go index 7f2dd4c..2e5bcde 100644 --- a/internal/api/core/v1/pods/list.go +++ b/internal/api/core/v1/pods/list.go @@ -9,7 +9,7 @@ import ( ) func (svc PodService) ListPods(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) utils.ListResources( r, diff --git a/internal/api/core/v1/pods/logs.go b/internal/api/core/v1/pods/logs.go index 2b61d27..7dcfef2 100644 --- a/internal/api/core/v1/pods/logs.go +++ b/internal/api/core/v1/pods/logs.go @@ -19,7 +19,7 @@ import ( // (flushWriter) that invokes the http.Flusher interface on every Write call to ensure // the data is immediately sent to the client. func (svc PodService) GetPodLogs(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) podName := r.PathParameter("name") podLogOptions := adapter.PodLogOptions{ diff --git a/internal/api/core/v1/pods/patch.go b/internal/api/core/v1/pods/patch.go index ebe3295..e065dcc 100644 --- a/internal/api/core/v1/pods/patch.go +++ b/internal/api/core/v1/pods/patch.go @@ -15,9 +15,9 @@ import ( ) func (svc PodService) PatchPod(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - podName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + podName := r.PathParameter("name") patch, err := io.ReadAll(r.Request.Body) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/pods/pods.go b/internal/api/core/v1/pods/pods.go index cad89fc..92f51ed 100644 --- a/internal/api/core/v1/pods/pods.go +++ b/internal/api/core/v1/pods/pods.go @@ -3,6 +3,7 @@ package pods import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" "github.com/portainer/k2d/internal/controller" ) @@ -30,6 +31,7 @@ func (svc PodService) RegisterPodAPI(ws *restful.WebService) { Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) ws.Route(ws.POST("/v1/namespaces/{namespace}/pods"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.CreatePod). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) @@ -38,6 +40,7 @@ func (svc PodService) RegisterPodAPI(ws *restful.WebService) { To(svc.ListPods)) ws.Route(ws.GET("/v1/namespaces/{namespace}/pods"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.ListPods). Param(ws.PathParameter("namespace", "namespace name").DataType("string"))) @@ -46,6 +49,7 @@ func (svc PodService) RegisterPodAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the pod").DataType("string"))) ws.Route(ws.DELETE("/v1/namespaces/{namespace}/pods/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.DeletePod). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the pod").DataType("string"))) @@ -55,6 +59,7 @@ func (svc PodService) RegisterPodAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the pod").DataType("string"))) ws.Route(ws.GET("/v1/namespaces/{namespace}/pods/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetPod). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the pod").DataType("string"))) @@ -66,6 +71,7 @@ func (svc PodService) RegisterPodAPI(ws *restful.WebService) { AddExtension("x-kubernetes-group-version-kind", podGVKExtension)) ws.Route(ws.PATCH("/v1/namespaces/{namespace}/pods/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PatchPod). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the pod").DataType("string")). @@ -73,6 +79,7 @@ func (svc PodService) RegisterPodAPI(ws *restful.WebService) { AddExtension("x-kubernetes-group-version-kind", podGVKExtension)) ws.Route(ws.GET("/v1/namespaces/{namespace}/pods/{name}/log"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetPodLogs)). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the pod").DataType("string")). diff --git a/internal/api/core/v1/secrets/create.go b/internal/api/core/v1/secrets/create.go index 33a945a..60459e2 100644 --- a/internal/api/core/v1/secrets/create.go +++ b/internal/api/core/v1/secrets/create.go @@ -13,10 +13,9 @@ import ( ) func (svc SecretService) CreateSecret(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) secret := &corev1.Secret{} - err := httputils.ParseJSONBody(r.Request, &secret) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/secrets/delete.go b/internal/api/core/v1/secrets/delete.go index 74a2aaa..e812e5a 100644 --- a/internal/api/core/v1/secrets/delete.go +++ b/internal/api/core/v1/secrets/delete.go @@ -10,9 +10,9 @@ import ( ) func (svc SecretService) DeleteSecret(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - secretName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + secretName := r.PathParameter("name") err := svc.adapter.DeleteSecret(secretName, namespace) if err != nil { utils.HttpError(r, w, http.StatusInternalServerError, fmt.Errorf("unable to delete secret: %w", err)) diff --git a/internal/api/core/v1/secrets/get.go b/internal/api/core/v1/secrets/get.go index c62cfe8..e44a5d7 100644 --- a/internal/api/core/v1/secrets/get.go +++ b/internal/api/core/v1/secrets/get.go @@ -11,7 +11,7 @@ import ( ) func (svc SecretService) GetSecret(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) secretName := r.PathParameter("name") secret, err := svc.adapter.GetSecret(secretName, namespace) diff --git a/internal/api/core/v1/secrets/list.go b/internal/api/core/v1/secrets/list.go index ff956eb..a8e87c7 100644 --- a/internal/api/core/v1/secrets/list.go +++ b/internal/api/core/v1/secrets/list.go @@ -12,7 +12,7 @@ import ( ) func (svc SecretService) ListSecrets(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) selectorParam := r.QueryParameter("labelSelector") selector, err := labels.Parse(selectorParam) diff --git a/internal/api/core/v1/secrets/patch.go b/internal/api/core/v1/secrets/patch.go index fba901f..cdf4e70 100644 --- a/internal/api/core/v1/secrets/patch.go +++ b/internal/api/core/v1/secrets/patch.go @@ -13,14 +13,13 @@ import ( "github.com/portainer/k2d/internal/controller" "github.com/portainer/k2d/internal/types" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/strategicpatch" ) func (svc SecretService) PatchSecret(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - secretName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + secretName := r.PathParameter("name") patch, err := io.ReadAll(r.Request.Body) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/secrets/put.go b/internal/api/core/v1/secrets/put.go index 7ee9295..590ede3 100644 --- a/internal/api/core/v1/secrets/put.go +++ b/internal/api/core/v1/secrets/put.go @@ -14,11 +14,10 @@ import ( ) func (svc SecretService) PutSecret(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - secretName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + secretName := r.PathParameter("name") secret := &corev1.Secret{} - err := httputils.ParseJSONBody(r.Request, &secret) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/secrets/secrets.go b/internal/api/core/v1/secrets/secrets.go index a2bfc74..212e305 100644 --- a/internal/api/core/v1/secrets/secrets.go +++ b/internal/api/core/v1/secrets/secrets.go @@ -3,6 +3,7 @@ package secrets import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" "github.com/portainer/k2d/internal/controller" ) @@ -30,6 +31,7 @@ func (svc SecretService) RegisterSecretAPI(ws *restful.WebService) { Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) ws.Route(ws.POST("/v1/namespaces/{namespace}/secrets"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.CreateSecret). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) @@ -39,6 +41,7 @@ func (svc SecretService) RegisterSecretAPI(ws *restful.WebService) { To(svc.ListSecrets)) ws.Route(ws.GET("/v1/namespaces/{namespace}/secrets"). + Filter(utils.NamespaceValidation(svc.adapter)). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.QueryParameter("labelSelector", "a selector to restrict the list of returned objects by their labels").DataType("string")). To(svc.ListSecrets)) @@ -48,6 +51,7 @@ func (svc SecretService) RegisterSecretAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the secret").DataType("string"))) ws.Route(ws.DELETE("/v1/namespaces/{namespace}/secrets/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.DeleteSecret). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the secret").DataType("string"))) @@ -57,6 +61,7 @@ func (svc SecretService) RegisterSecretAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the secret").DataType("string"))) ws.Route(ws.GET("/v1/namespaces/{namespace}/secrets/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetSecret). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the secret").DataType("string"))) @@ -68,6 +73,7 @@ func (svc SecretService) RegisterSecretAPI(ws *restful.WebService) { AddExtension("x-kubernetes-group-version-kind", secretGVKExtension)) ws.Route(ws.PATCH("/v1/namespaces/{namespace}/secrets/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PatchSecret). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the secret").DataType("string")). @@ -81,6 +87,7 @@ func (svc SecretService) RegisterSecretAPI(ws *restful.WebService) { AddExtension("x-kubernetes-group-version-kind", secretGVKExtension)) ws.Route(ws.PUT("/v1/namespaces/{namespace}/secrets/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PutSecret). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the secret").DataType("string")). diff --git a/internal/api/core/v1/services/create.go b/internal/api/core/v1/services/create.go index 6aec424..00f43d8 100644 --- a/internal/api/core/v1/services/create.go +++ b/internal/api/core/v1/services/create.go @@ -13,10 +13,9 @@ import ( ) func (svc ServiceService) CreateService(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) service := &corev1.Service{} - err := httputils.ParseJSONBody(r.Request, &service) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/services/delete.go b/internal/api/core/v1/services/delete.go index 2ac96f2..4011f69 100644 --- a/internal/api/core/v1/services/delete.go +++ b/internal/api/core/v1/services/delete.go @@ -10,9 +10,9 @@ import ( ) func (svc ServiceService) DeleteService(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - serviceName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + serviceName := r.PathParameter("name") err := svc.adapter.DeleteService(r.Request.Context(), serviceName, namespace) if err != nil { utils.HttpError(r, w, http.StatusInternalServerError, fmt.Errorf("unable to delete service: %w", err)) diff --git a/internal/api/core/v1/services/get.go b/internal/api/core/v1/services/get.go index 36264e1..56375dc 100644 --- a/internal/api/core/v1/services/get.go +++ b/internal/api/core/v1/services/get.go @@ -11,7 +11,7 @@ import ( ) func (svc ServiceService) GetService(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) serviceName := r.PathParameter("name") service, err := svc.adapter.GetService(r.Request.Context(), serviceName, namespace) diff --git a/internal/api/core/v1/services/list.go b/internal/api/core/v1/services/list.go index cd8376d..fff6f6c 100644 --- a/internal/api/core/v1/services/list.go +++ b/internal/api/core/v1/services/list.go @@ -9,7 +9,7 @@ import ( ) func (svc ServiceService) ListServices(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") + namespace := utils.GetNamespaceFromRequest(r) utils.ListResources( r, diff --git a/internal/api/core/v1/services/patch.go b/internal/api/core/v1/services/patch.go index b01ce10..d6263d9 100644 --- a/internal/api/core/v1/services/patch.go +++ b/internal/api/core/v1/services/patch.go @@ -15,9 +15,9 @@ import ( ) func (svc ServiceService) PatchService(r *restful.Request, w *restful.Response) { - namespace := r.PathParameter("namespace") - serviceName := r.PathParameter("name") + namespace := utils.GetNamespaceFromRequest(r) + serviceName := r.PathParameter("name") patch, err := io.ReadAll(r.Request.Body) if err != nil { utils.HttpError(r, w, http.StatusBadRequest, fmt.Errorf("unable to parse request body: %w", err)) diff --git a/internal/api/core/v1/services/services.go b/internal/api/core/v1/services/services.go index c9744e2..6af2bb1 100644 --- a/internal/api/core/v1/services/services.go +++ b/internal/api/core/v1/services/services.go @@ -3,6 +3,7 @@ package services import ( "github.com/emicklei/go-restful/v3" "github.com/portainer/k2d/internal/adapter" + "github.com/portainer/k2d/internal/api/utils" "github.com/portainer/k2d/internal/controller" ) @@ -30,6 +31,7 @@ func (svc ServiceService) RegisterServiceAPI(ws *restful.WebService) { Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) ws.Route(ws.POST("/v1/namespaces/{namespace}/services"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.CreateService). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.QueryParameter("dryRun", "when present, indicates that modifications should not be persisted").DataType("string"))) @@ -38,6 +40,7 @@ func (svc ServiceService) RegisterServiceAPI(ws *restful.WebService) { To(svc.ListServices)) ws.Route(ws.GET("/v1/namespaces/{namespace}/services"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.ListServices). Param(ws.PathParameter("namespace", "namespace name").DataType("string"))) @@ -46,6 +49,7 @@ func (svc ServiceService) RegisterServiceAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the service").DataType("string"))) ws.Route(ws.DELETE("/v1/namespaces/{namespace}/services/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.DeleteService). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the service").DataType("string"))) @@ -55,6 +59,7 @@ func (svc ServiceService) RegisterServiceAPI(ws *restful.WebService) { Param(ws.PathParameter("name", "name of the service").DataType("string"))) ws.Route(ws.GET("/v1/namespaces/{namespace}/services/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.GetService). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the service").DataType("string"))) @@ -66,6 +71,7 @@ func (svc ServiceService) RegisterServiceAPI(ws *restful.WebService) { AddExtension("x-kubernetes-group-version-kind", serviceGVKExtension)) ws.Route(ws.PATCH("/v1/namespaces/{namespace}/services/{name}"). + Filter(utils.NamespaceValidation(svc.adapter)). To(svc.PatchService). Param(ws.PathParameter("namespace", "namespace name").DataType("string")). Param(ws.PathParameter("name", "name of the service").DataType("string")). diff --git a/internal/api/utils/middleware.go b/internal/api/utils/middleware.go new file mode 100644 index 0000000..65cc732 --- /dev/null +++ b/internal/api/utils/middleware.go @@ -0,0 +1,60 @@ +package utils + +import ( + "errors" + "fmt" + "net/http" + + "github.com/emicklei/go-restful/v3" + "github.com/portainer/k2d/internal/adapter" + adaptererr "github.com/portainer/k2d/internal/adapter/errors" + "github.com/portainer/k2d/internal/logging" + apierr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// NamespaceValidation is a filter function that validates the existence of a namespace in the k2d environment. +// The function attempts to retrieve the namespace specified in the request path and checks its existence. +// If the namespace is not found, it responds with an HTTP 404 status and an appropriate error message. +// If an internal server error occurs, it responds with an HTTP 500 status. +// If the namespace is valid, the function adds it as an attribute to the request object and continues +// the request processing by invoking the next filter in the chain. +// +// Parameters: +// - adapter: A pointer to an initialized KubeDockerAdapter object. +// +// Returns: +// - restful.FilterFunction: A function conforming to the FilterFunction type from the go-restful package. +func NamespaceValidation(adapter *adapter.KubeDockerAdapter) restful.FilterFunction { + return func(r *restful.Request, w *restful.Response, chain *restful.FilterChain) { + namespace := r.PathParameter("namespace") + + _, err := adapter.GetNamespace(r.Request.Context(), namespace) + if err != nil { + if errors.Is(err, adaptererr.ErrResourceNotFound) { + notFoundErr := apierr.NewNotFound( + schema.GroupResource{Group: "", Resource: "namespaces"}, + namespace, + ) + + notFoundErr.ErrStatus.TypeMeta = metav1.TypeMeta{ + Kind: "Status", + APIVersion: "v1", + } + + logger := logging.LoggerFromContext(r.Request.Context()) + logger.Errorw("namespace not found", "namespace", namespace) + + w.WriteHeaderAndEntity(http.StatusNotFound, notFoundErr.ErrStatus) + return + } + + HttpError(r, w, http.StatusInternalServerError, fmt.Errorf("unable to get namespace: %w", err)) + return + } + + r.SetAttribute("namespace", namespace) + chain.ProcessFilter(r, w) + } +} diff --git a/internal/api/utils/request.go b/internal/api/utils/request.go new file mode 100644 index 0000000..19e9be9 --- /dev/null +++ b/internal/api/utils/request.go @@ -0,0 +1,24 @@ +package utils + +import ( + "github.com/emicklei/go-restful/v3" +) + +// GetNamespaceFromRequest attempts to obtain the namespace from a restful.Request object. +// Initially, it looks for an attribute named "namespace" that may have been set by preceding middleware (e.g., NamespaceValidation). +// If this attribute is either not found or empty, the function falls back to retrieving the namespace from the request's path parameters. +// +// Parameters: +// - r: A pointer to a restful.Request object containing either an attribute or a path parameter named "namespace". +// +// Returns: +// - string: The namespace retrieved from the request attributes or path parameters. Returns an empty string if not found. +func GetNamespaceFromRequest(r *restful.Request) string { + namespace, ok := r.Attribute("namespace").(string) + + if !ok || namespace == "" { + namespace = r.PathParameter("namespace") + } + + return namespace +}