mirror of https://github.com/portainer/portainer
refactor(k8s): update based on review
parent
f4b795bbbb
commit
12c8332ae2
|
@ -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…
Reference in New Issue