refactor(k8s): update based on review

pull/12062/head
stevensbkang 3 months ago
parent f4b795bbbb
commit 12c8332ae2
No known key found for this signature in database

@ -168,9 +168,15 @@ func (handler *Handler) kubeClientMiddleware(next http.Handler) http.Handler {
isKubeAdmin := true
nonAdminNamespaces := []string{}
if user.Role != portainer.AdministratorRole {
nonAdminNamespaces, err = cli.GetNonAdminNamespaces(int(user.ID), endpoint, handler.KubernetesClientFactory)
pcli, err := handler.KubernetesClientFactory.GetPrivilegedKubeClient(endpoint)
if err != nil {
httperror.WriteError(w, http.StatusInternalServerError, "an error occurred during the IsAdmin operation, unable to retrieve non-admin namespaces. Error: ", err)
httperror.WriteError(w, http.StatusInternalServerError, "an error occurred during the kubeClientMiddleware operation, unable to get privileged kube client to grab all namespaces. Error: ", err)
return
}
nonAdminNamespaces, err = pcli.GetNonAdminNamespaces(int(user.ID), endpoint.Kubernetes.Configuration.RestrictDefaultNamespace)
if err != nil {
httperror.WriteError(w, http.StatusInternalServerError, "an error occurred during the kubeClientMiddleware operation, unable to retrieve non-admin namespaces. Error: ", err)
return
}
isKubeAdmin = false

@ -118,19 +118,14 @@ func (kcl *KubeClient) UpdateNamespaceAccessPolicies(accessPolicies map[string]p
}
// GetNonAdminNamespaces retrieves namespaces for a non-admin user, excluding the default namespace if restricted.
func GetNonAdminNamespaces(userID int, endpoint *portainer.Endpoint, clientFactory *ClientFactory) ([]string, error) {
kcl, err := clientFactory.GetPrivilegedKubeClient(endpoint)
if err != nil {
return nil, fmt.Errorf("an error occurred during the getNonAdminNamespaces operation, unable to get privileged kube client: %w", err)
}
func (kcl *KubeClient) GetNonAdminNamespaces(userID int, isRestrictDefaultNamespace bool) ([]string, error) {
accessPolicies, err := kcl.GetNamespaceAccessPolicies()
if err != nil {
return nil, fmt.Errorf("an error occurred during the getNonAdminNamespaces operation, unable to get namespace access policies via portainer-config. check if portainer-config configMap exists in the Kubernetes cluster: %w", err)
}
nonAdminNamespaces := []string{}
if !endpoint.Kubernetes.Configuration.RestrictDefaultNamespace {
if !isRestrictDefaultNamespace {
nonAdminNamespaces = append(nonAdminNamespaces, defaultNamespace)
}

@ -9,7 +9,6 @@ import (
"github.com/pkg/errors"
portainer "github.com/portainer/portainer/api"
portaineree "github.com/portainer/portainer/api"
models "github.com/portainer/portainer/api/http/models/kubernetes"
"github.com/portainer/portainer/api/stacks/stackutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
@ -37,19 +36,15 @@ func defaultSystemNamespaces() map[string]struct{} {
}
// GetNamespaces gets the namespaces in the current k8s environment(endpoint).
// if the user is an admin, all namespaces in the current k8s environment(endpoint) are fetched using the fetchNamespaces function.
// otherwise, namespaces the non-admin user has access to will be used to filter the namespaces based on the allowed namespaces.
func (kcl *KubeClient) GetNamespaces() (map[string]portainer.K8sNamespaceInfo, error) {
if kcl.IsKubeAdmin {
return kcl.fetchNamespacesForAdmin()
return kcl.fetchNamespaces()
}
return kcl.fetchNamespacesForNonAdmin()
}
// fetchNamespacesForAdmin gets the namespaces in the current k8s environment(endpoint) for the admin user.
// The kube client must have cluster scope read access to do this.
func (kcl *KubeClient) fetchNamespacesForAdmin() (map[string]portainer.K8sNamespaceInfo, error) {
return kcl.fetchNamespaces()
}
// fetchNamespacesForNonAdmin gets the namespaces in the current k8s environment(endpoint) for the non-admin user.
func (kcl *KubeClient) fetchNamespacesForNonAdmin() (map[string]portainer.K8sNamespaceInfo, error) {
log.Debug().Msgf("Fetching namespaces for non-admin user: %v", kcl.NonAdminNamespaces)
@ -63,7 +58,7 @@ func (kcl *KubeClient) fetchNamespacesForNonAdmin() (map[string]portainer.K8sNam
return nil, fmt.Errorf("an error occurred during the fetchNamespacesForNonAdmin operation, unable to list namespaces for the non-admin user: %w", err)
}
nonAdminNamespaceSet := kcl.BuildNonAdminNamespacesMap()
nonAdminNamespaceSet := kcl.buildNonAdminNamespacesMap()
results := make(map[string]portainer.K8sNamespaceInfo)
for _, namespace := range namespaces {
if _, exists := nonAdminNamespaceSet[namespace.Name]; exists {
@ -259,7 +254,7 @@ func (kcl *KubeClient) DeleteNamespace(namespace string) error {
}
// CombineNamespacesWithResourceQuotas combines namespaces with resource quotas where matching is based on "portainer-rq-"+namespace.Name
func (kcl *KubeClient) CombineNamespacesWithResourceQuotas(namespaces map[string]portaineree.K8sNamespaceInfo, w http.ResponseWriter) (map[string]portainer.K8sNamespaceInfo, *httperror.HandlerError) {
func (kcl *KubeClient) CombineNamespacesWithResourceQuotas(namespaces map[string]portainer.K8sNamespaceInfo, w http.ResponseWriter) (map[string]portainer.K8sNamespaceInfo, *httperror.HandlerError) {
resourceQuotas, err := kcl.GetResourceQuotas("")
if err != nil {
return nil, httperror.InternalServerError("an error occurred during the CombineNamespacesWithResourceQuotas operation, unable to retrieve resource quotas from the Kubernetes for an admin user. Error: ", err)
@ -273,7 +268,7 @@ func (kcl *KubeClient) CombineNamespacesWithResourceQuotas(namespaces map[string
}
// CombineNamespaceWithResourceQuota combines a namespace with a resource quota prefixed with "portainer-rq-"+namespace.Name
func (kcl *KubeClient) CombineNamespaceWithResourceQuota(namespace portaineree.K8sNamespaceInfo, w http.ResponseWriter) *httperror.HandlerError {
func (kcl *KubeClient) CombineNamespaceWithResourceQuota(namespace portainer.K8sNamespaceInfo, w http.ResponseWriter) *httperror.HandlerError {
resourceQuota, err := kcl.GetPortainerResourceQuota(namespace.Name)
if err != nil && !k8serrors.IsNotFound(err) {
return httperror.InternalServerError(fmt.Sprintf("an error occurred during the CombineNamespaceWithResourceQuota operation, unable to retrieve the resource quota associated with the namespace: %s for a non-admin user. Error: ", namespace.Name), err)
@ -283,7 +278,9 @@ func (kcl *KubeClient) CombineNamespaceWithResourceQuota(namespace portaineree.K
return response.JSON(w, namespace)
}
func (kcl *KubeClient) BuildNonAdminNamespacesMap() map[string]struct{} {
// buildNonAdminNamespacesMap builds a map of non-admin namespaces.
// the map is used to filter the namespaces based on the allowed namespaces.
func (kcl *KubeClient) buildNonAdminNamespacesMap() map[string]struct{} {
nonAdminNamespaceSet := make(map[string]struct{}, len(kcl.NonAdminNamespaces))
for _, namespace := range kcl.NonAdminNamespaces {
nonAdminNamespaceSet[namespace] = struct{}{}
@ -291,3 +288,13 @@ func (kcl *KubeClient) BuildNonAdminNamespacesMap() map[string]struct{} {
return nonAdminNamespaceSet
}
// ConvertNamespaceMapToSlice converts the namespace map to a slice of namespaces.
// this is used to for the API response.
func (kcl *KubeClient) ConvertNamespaceMapToSlice(namespaces map[string]portainer.K8sNamespaceInfo) []portainer.K8sNamespaceInfo {
namespaceSlice := make([]portainer.K8sNamespaceInfo, 0, len(namespaces))
for _, namespace := range namespaces {
namespaceSlice = append(namespaceSlice, namespace)
}
return namespaceSlice
}

@ -11,20 +11,15 @@ import (
)
// GetResourceQuotas gets all resource quotas in the current k8s environment(endpoint).
// The kube client must have cluster scope read access to do this.
// if the user is an admin, all resource quotas in all namespaces are fetched.
// otherwise, namespaces the non-admin user has access to will be used to filter the resource quotas.
func (kcl *KubeClient) GetResourceQuotas(namespace string) (*[]corev1.ResourceQuota, error) {
if kcl.IsKubeAdmin {
return kcl.fetchResourceQuotasForAdmin(namespace)
return kcl.fetchResourceQuotas(namespace)
}
return kcl.fetchResourceQuotasForNonAdmin(namespace)
}
// fetchResourceQuotasForAdmin gets the resource quotas in the current k8s environment(endpoint) for an admin user.
// The kube client must have cluster scope read access to do this.
func (kcl *KubeClient) fetchResourceQuotasForAdmin(namespace string) (*[]corev1.ResourceQuota, error) {
return kcl.fetchResourceQuotas(namespace)
}
// fetchResourceQuotasForNonAdmin gets the resource quotas in the current k8s environment(endpoint) for a non-admin user.
// the role of the user must have read access to the resource quotas in the defined namespaces.
func (kcl *KubeClient) fetchResourceQuotasForNonAdmin(namespace string) (*[]corev1.ResourceQuota, error) {
@ -39,7 +34,7 @@ func (kcl *KubeClient) fetchResourceQuotasForNonAdmin(namespace string) (*[]core
return nil, err
}
nonAdminNamespaceSet := kcl.BuildNonAdminNamespacesMap()
nonAdminNamespaceSet := kcl.buildNonAdminNamespacesMap()
results := []corev1.ResourceQuota{}
for _, resourceQuota := range *resourceQuotas {
if _, exists := nonAdminNamespaceSet[resourceQuota.Namespace]; exists {
@ -59,8 +54,8 @@ func (kcl *KubeClient) fetchResourceQuotas(namespace string) (*[]corev1.Resource
return &resourceQuotas.Items, nil
}
// GetPortainerResourceQuota gets the resource quota for the portainer namespace.
// The resource quota is prefixed with "portainer-rq-".
// GetPortainerResourceQuota gets the resource quota prefixed with "portainer-rq-" in a specific namespace.
// this is to fetch a specific resource quota created by Portainer.
func (kcl *KubeClient) GetPortainerResourceQuota(namespace string) (*corev1.ResourceQuota, error) {
return kcl.cli.CoreV1().ResourceQuotas(namespace).Get(context.TODO(), "portainer-rq-"+namespace, metav1.GetOptions{})
}
@ -83,8 +78,7 @@ func (kcl *KubeClient) UpdateNamespacesWithResourceQuotas(namespaces map[string]
return namespacesWithQuota
}
// GetResourceQuotaFromNamespace updates the namespace.ResourceQuota field with the resource quota information.
// The resource quota is matched with the namespace and prefixed with "portainer-rq-".
// GetResourceQuotaFromNamespace gets the resource quota in a specific namespace where the resource quota's name is prefixed with "portainer-rq-".
func (kcl *KubeClient) GetResourceQuotaFromNamespace(namespace portaineree.K8sNamespaceInfo, resourceQuotas []corev1.ResourceQuota) *corev1.ResourceQuota {
for _, resourceQuota := range resourceQuotas {
if resourceQuota.ObjectMeta.Namespace == namespace.Name && resourceQuota.ObjectMeta.Name == "portainer-rq-"+namespace.Name {

Loading…
Cancel
Save