From a550bfaedb1de1ea689a6fba0956116dba796f36 Mon Sep 17 00:00:00 2001 From: Prabhat Khera <91852476+prabhat-org@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:14:54 +1300 Subject: [PATCH] fix showing namespaces for standard user (#7917) --- api/http/handler/kubernetes/handler.go | 1 + api/http/handler/kubernetes/namespaces.go | 37 ++++++++++++++++++++++ api/kubernetes/cli/namespace.go | 15 +++++++++ api/portainer.go | 1 + app/react/kubernetes/namespaces/queries.ts | 18 ++++++++++- app/react/kubernetes/namespaces/service.ts | 12 +++---- 6 files changed, 77 insertions(+), 7 deletions(-) diff --git a/api/http/handler/kubernetes/handler.go b/api/http/handler/kubernetes/handler.go index 7d46c00d3..f3f1a688b 100644 --- a/api/http/handler/kubernetes/handler.go +++ b/api/http/handler/kubernetes/handler.go @@ -62,6 +62,7 @@ func NewHandler(bouncer *security.RequestBouncer, authorizationService *authoriz endpointRouter.Path("/namespaces").Handler(httperror.LoggerHandler(h.updateKubernetesNamespace)).Methods(http.MethodPut) endpointRouter.Path("/namespaces").Handler(httperror.LoggerHandler(h.getKubernetesNamespaces)).Methods(http.MethodGet) endpointRouter.Path("/namespace/{namespace}").Handler(httperror.LoggerHandler(h.deleteKubernetesNamespaces)).Methods(http.MethodDelete) + endpointRouter.Path("/namespaces/{namespace}").Handler(httperror.LoggerHandler(h.getKubernetesNamespace)).Methods(http.MethodGet) // namespaces // in the future this piece of code might be in another package (or a few different packages - namespaces/namespace?) diff --git a/api/http/handler/kubernetes/namespaces.go b/api/http/handler/kubernetes/namespaces.go index 68ba70261..d2e4d0c17 100644 --- a/api/http/handler/kubernetes/namespaces.go +++ b/api/http/handler/kubernetes/namespaces.go @@ -40,6 +40,43 @@ func (handler *Handler) getKubernetesNamespaces(w http.ResponseWriter, r *http.R return response.JSON(w, namespaces) } +func (handler *Handler) getKubernetesNamespace(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { + endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id") + if err != nil { + return httperror.BadRequest( + "Invalid environment identifier route variable", + err, + ) + } + + cli, ok := handler.KubernetesClientFactory.GetProxyKubeClient( + strconv.Itoa(endpointID), r.Header.Get("Authorization"), + ) + if !ok { + return httperror.InternalServerError( + "Failed to lookup KubeClient", + nil, + ) + } + + ns, err := request.RetrieveRouteVariableValue(r, "namespace") + if err != nil { + return httperror.BadRequest( + "Invalid namespace identifier route variable", + err, + ) + } + namespace, err := cli.GetNamespace(ns) + if err != nil { + return httperror.InternalServerError( + "Unable to retrieve namespace", + err, + ) + } + + return response.JSON(w, namespace) +} + func (handler *Handler) createKubernetesNamespace(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id") if err != nil { diff --git a/api/kubernetes/cli/namespace.go b/api/kubernetes/cli/namespace.go index 18701822d..0baacf09a 100644 --- a/api/kubernetes/cli/namespace.go +++ b/api/kubernetes/cli/namespace.go @@ -44,6 +44,21 @@ func (kcl *KubeClient) GetNamespaces() (map[string]portainer.K8sNamespaceInfo, e return results, nil } +// GetNamespace gets the namespace in the current k8s environment(endpoint). +func (kcl *KubeClient) GetNamespace(name string) (portainer.K8sNamespaceInfo, error) { + namespace, err := kcl.cli.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return portainer.K8sNamespaceInfo{}, err + } + + result := portainer.K8sNamespaceInfo{ + IsSystem: isSystemNamespace(*namespace), + IsDefault: namespace.Name == defaultNamespace, + } + + return result, nil +} + // CreateIngress creates a new ingress in a given namespace in a k8s endpoint. func (kcl *KubeClient) CreateNamespace(info models.K8sNamespaceDetails) error { client := kcl.cli.CoreV1().Namespaces() diff --git a/api/portainer.go b/api/portainer.go index 4fbd3d61c..3afa7322a 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -1357,6 +1357,7 @@ type ( CreateNamespace(info models.K8sNamespaceDetails) error UpdateNamespace(info models.K8sNamespaceDetails) error GetNamespaces() (map[string]K8sNamespaceInfo, error) + GetNamespace(string) (K8sNamespaceInfo, error) DeleteNamespace(namespace string) error GetConfigMapsAndSecrets(namespace string) ([]models.K8sConfigMapOrSecret, error) GetIngressControllers() (models.K8sIngressControllers, error) diff --git a/app/react/kubernetes/namespaces/queries.ts b/app/react/kubernetes/namespaces/queries.ts index 00d1b65cd..f79a6c555 100644 --- a/app/react/kubernetes/namespaces/queries.ts +++ b/app/react/kubernetes/namespaces/queries.ts @@ -2,13 +2,29 @@ import { useQuery } from 'react-query'; import { EnvironmentId } from '@/react/portainer/environments/types'; import { error as notifyError } from '@/portainer/services/notifications'; +import { getIngresses } from '@/kubernetes/react/views/networks/ingresses/service'; import { getNamespaces, getNamespace } from './service'; +import { Namespaces } from './types'; export function useNamespaces(environmentId: EnvironmentId) { return useQuery( ['environments', environmentId, 'kubernetes', 'namespaces'], - () => getNamespaces(environmentId), + async () => { + const namespaces = await getNamespaces(environmentId); + const settledNamespacesPromise = await Promise.allSettled( + Object.keys(namespaces).map((namespace) => + getIngresses(environmentId, namespace).then(() => namespace) + ) + ); + const ns: Namespaces = {}; + settledNamespacesPromise.forEach((namespace) => { + if (namespace.status === 'fulfilled') { + ns[namespace.value] = namespaces[namespace.value]; + } + }); + return ns; + }, { onError: (err) => { notifyError('Failure', err as Error, 'Unable to get namespaces.'); diff --git a/app/react/kubernetes/namespaces/service.ts b/app/react/kubernetes/namespaces/service.ts index 43612d62e..57e91689d 100644 --- a/app/react/kubernetes/namespaces/service.ts +++ b/app/react/kubernetes/namespaces/service.ts @@ -8,23 +8,23 @@ export async function getNamespace( namespace: string ) { try { - const { data: ingress } = await axios.get( + const { data: ns } = await axios.get( buildUrl(environmentId, namespace) ); - return ingress; + return ns; } catch (e) { - throw parseAxiosError(e as Error, 'Unable to retrieve network details'); + throw parseAxiosError(e as Error, 'Unable to retrieve namespace'); } } export async function getNamespaces(environmentId: EnvironmentId) { try { - const { data: ingresses } = await axios.get( + const { data: namespaces } = await axios.get( buildUrl(environmentId) ); - return ingresses; + return namespaces; } catch (e) { - throw parseAxiosError(e as Error, 'Unable to retrieve network details'); + throw parseAxiosError(e as Error, 'Unable to retrieve namespaces'); } }