diff --git a/api/kubernetes/cli/access.go b/api/kubernetes/cli/access.go index 6f254c296..30ef19462 100644 --- a/api/kubernetes/cli/access.go +++ b/api/kubernetes/cli/access.go @@ -145,21 +145,33 @@ func (kcl *KubeClient) GetNonAdminNamespaces(userID int, teamIDs []int, isRestri } // GetIsKubeAdmin retrieves true if client is admin -func (client *KubeClient) GetIsKubeAdmin() bool { - return client.IsKubeAdmin +func (kcl *KubeClient) GetIsKubeAdmin() bool { + kcl.mu.Lock() + defer kcl.mu.Unlock() + + return kcl.isKubeAdmin } // UpdateIsKubeAdmin sets whether the kube client is admin -func (client *KubeClient) SetIsKubeAdmin(isKubeAdmin bool) { - client.IsKubeAdmin = isKubeAdmin +func (kcl *KubeClient) SetIsKubeAdmin(isKubeAdmin bool) { + kcl.mu.Lock() + defer kcl.mu.Unlock() + + kcl.isKubeAdmin = isKubeAdmin } // GetClientNonAdminNamespaces retrieves non-admin namespaces -func (client *KubeClient) GetClientNonAdminNamespaces() []string { - return client.NonAdminNamespaces +func (kcl *KubeClient) GetClientNonAdminNamespaces() []string { + kcl.mu.Lock() + defer kcl.mu.Unlock() + + return kcl.nonAdminNamespaces } // UpdateClientNonAdminNamespaces sets the client non admin namespace list -func (client *KubeClient) SetClientNonAdminNamespaces(nonAdminNamespaces []string) { - client.NonAdminNamespaces = nonAdminNamespaces +func (kcl *KubeClient) SetClientNonAdminNamespaces(nonAdminNamespaces []string) { + kcl.mu.Lock() + defer kcl.mu.Unlock() + + kcl.nonAdminNamespaces = nonAdminNamespaces } diff --git a/api/kubernetes/cli/access_test.go b/api/kubernetes/cli/access_test.go index 09bb28d9e..2b0285d87 100644 --- a/api/kubernetes/cli/access_test.go +++ b/api/kubernetes/cli/access_test.go @@ -5,7 +5,9 @@ import ( "testing" portainer "github.com/portainer/portainer/api" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ktypes "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kfake "k8s.io/client-go/kubernetes/fake" @@ -65,3 +67,27 @@ func Test_NamespaceAccessPoliciesDeleteNamespace_updatesPortainerConfig_whenConf }) } } + +func TestKubeAdmin(t *testing.T) { + kcl := &KubeClient{} + require.False(t, kcl.GetIsKubeAdmin()) + + kcl.SetIsKubeAdmin(true) + require.True(t, kcl.GetIsKubeAdmin()) + + kcl.SetIsKubeAdmin(false) + require.False(t, kcl.GetIsKubeAdmin()) +} + +func TestClientNonAdminNamespaces(t *testing.T) { + kcl := &KubeClient{} + + require.Empty(t, kcl.GetClientNonAdminNamespaces()) + + nss := []string{"ns1", "ns2"} + kcl.SetClientNonAdminNamespaces(nss) + require.Equal(t, nss, kcl.GetClientNonAdminNamespaces()) + + kcl.SetClientNonAdminNamespaces([]string{}) + require.Empty(t, kcl.GetClientNonAdminNamespaces()) +} diff --git a/api/kubernetes/cli/applications.go b/api/kubernetes/cli/applications.go index e8807afd4..14fc68447 100644 --- a/api/kubernetes/cli/applications.go +++ b/api/kubernetes/cli/applications.go @@ -28,7 +28,7 @@ type PortainerApplicationResources struct { // if the user is an admin, all namespaces in the current k8s environment(endpoint) are fetched using the fetchApplications function. // otherwise, namespaces the non-admin user has access to will be used to filter the applications based on the allowed namespaces. func (kcl *KubeClient) GetApplications(namespace, nodeName string) ([]models.K8sApplication, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchApplications(namespace, nodeName) } @@ -64,9 +64,13 @@ func (kcl *KubeClient) fetchApplications(namespace, nodeName string) ([]models.K // fetchApplicationsForNonAdmin fetches the applications in the namespaces the user has access to. // This function is called when the user is not an admin. func (kcl *KubeClient) fetchApplicationsForNonAdmin(namespace, nodeName string) ([]models.K8sApplication, error) { - log.Debug().Msgf("Fetching applications for non-admin user: %v", kcl.NonAdminNamespaces) + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() - if len(kcl.NonAdminNamespaces) == 0 { + log.Debug(). + Strs("non_admin_namespaces", nonAdminNamespaces). + Msg("fetching applications for non-admin user") + + if len(nonAdminNamespaces) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/applications_test.go b/api/kubernetes/cli/applications_test.go index 81a5cfb71..490b77fb7 100644 --- a/api/kubernetes/cli/applications_test.go +++ b/api/kubernetes/cli/applications_test.go @@ -310,7 +310,7 @@ func TestGetApplications(t *testing.T) { kubeClient := &KubeClient{ cli: fakeClient, instanceID: "test-instance", - IsKubeAdmin: true, + isKubeAdmin: true, } // Test cases @@ -385,8 +385,8 @@ func TestGetApplications(t *testing.T) { kubeClient := &KubeClient{ cli: fakeClient, instanceID: "test-instance", - IsKubeAdmin: false, - NonAdminNamespaces: []string{namespace1}, + isKubeAdmin: false, + nonAdminNamespaces: []string{namespace1}, } // Test that only resources from allowed namespace are returned @@ -445,7 +445,7 @@ func TestGetApplications(t *testing.T) { kubeClient := &KubeClient{ cli: fakeClient, instanceID: "test-instance", - IsKubeAdmin: true, + isKubeAdmin: true, } // Test filtering by node name diff --git a/api/kubernetes/cli/client.go b/api/kubernetes/cli/client.go index 550ade1d3..6a585a797 100644 --- a/api/kubernetes/cli/client.go +++ b/api/kubernetes/cli/client.go @@ -42,8 +42,8 @@ type ( cli kubernetes.Interface instanceID string mu sync.Mutex - IsKubeAdmin bool - NonAdminNamespaces []string + isKubeAdmin bool + nonAdminNamespaces []string } ) @@ -147,6 +147,7 @@ func (factory *ClientFactory) GetProxyKubeClient(endpointID, userID string) (*Ku if ok { return client.(*KubeClient), true } + return nil, false } @@ -179,8 +180,8 @@ func (factory *ClientFactory) CreateKubeClientFromKubeConfig(clusterID string, k return &KubeClient{ cli: cli, instanceID: factory.instanceID, - IsKubeAdmin: IsKubeAdmin, - NonAdminNamespaces: NonAdminNamespaces, + isKubeAdmin: IsKubeAdmin, + nonAdminNamespaces: NonAdminNamespaces, }, nil } @@ -193,7 +194,7 @@ func (factory *ClientFactory) createCachedPrivilegedKubeClient(endpoint *portain return &KubeClient{ cli: cli, instanceID: factory.instanceID, - IsKubeAdmin: true, + isKubeAdmin: true, }, nil } @@ -371,6 +372,7 @@ func (factory *ClientFactory) MigrateEndpointIngresses(e *portainer.Endpoint, da log.Error().Err(err).Msgf("Error getting ingresses in environment %d", environment.ID) return err } + for _, ingress := range ingresses { oldController, ok := ingress.Annotations["ingress.portainer.io/ingress-type"] if !ok { diff --git a/api/kubernetes/cli/cluster_role.go b/api/kubernetes/cli/cluster_role.go index 8c375d727..9d86051ad 100644 --- a/api/kubernetes/cli/cluster_role.go +++ b/api/kubernetes/cli/cluster_role.go @@ -16,7 +16,7 @@ import ( // GetClusterRoles gets all the clusterRoles for at the cluster level in a k8s endpoint. // It returns a list of K8sClusterRole objects. func (kcl *KubeClient) GetClusterRoles() ([]models.K8sClusterRole, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchClusterRoles() } diff --git a/api/kubernetes/cli/cluster_role_binding.go b/api/kubernetes/cli/cluster_role_binding.go index 1c0824e6c..47aaa7660 100644 --- a/api/kubernetes/cli/cluster_role_binding.go +++ b/api/kubernetes/cli/cluster_role_binding.go @@ -16,7 +16,7 @@ import ( // GetClusterRoleBindings gets all the clusterRoleBindings for at the cluster level in a k8s endpoint. // It returns a list of K8sClusterRoleBinding objects. func (kcl *KubeClient) GetClusterRoleBindings() ([]models.K8sClusterRoleBinding, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchClusterRoleBindings() } diff --git a/api/kubernetes/cli/configmap.go b/api/kubernetes/cli/configmap.go index fafa81346..2b5dd8baf 100644 --- a/api/kubernetes/cli/configmap.go +++ b/api/kubernetes/cli/configmap.go @@ -16,18 +16,23 @@ import ( // if the user is an admin, all configMaps in the current k8s environment(endpoint) are fetched using the fetchConfigMaps function. // otherwise, namespaces the non-admin user has access to will be used to filter the configMaps based on the allowed namespaces. func (kcl *KubeClient) GetConfigMaps(namespace string) ([]models.K8sConfigMap, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchConfigMaps(namespace) } + return kcl.fetchConfigMapsForNonAdmin(namespace) } // fetchConfigMapsForNonAdmin fetches the configMaps in the namespaces the user has access to. // This function is called when the user is not an admin. func (kcl *KubeClient) fetchConfigMapsForNonAdmin(namespace string) ([]models.K8sConfigMap, error) { - log.Debug().Msgf("Fetching configMaps for non-admin user: %v", kcl.NonAdminNamespaces) + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() - if len(kcl.NonAdminNamespaces) == 0 { + log.Debug(). + Strs("non_admin_namespaces", nonAdminNamespaces). + Msg("fetching configMaps for non-admin user") + + if len(nonAdminNamespaces) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/cronjob.go b/api/kubernetes/cli/cronjob.go index f00be6dc7..c81b4f6fc 100644 --- a/api/kubernetes/cli/cronjob.go +++ b/api/kubernetes/cli/cronjob.go @@ -15,7 +15,7 @@ import ( // If the user is a kube admin, it returns all cronjobs in the namespace // Otherwise, it returns only the cronjobs in the non-admin namespaces func (kcl *KubeClient) GetCronJobs(namespace string) ([]models.K8sCronJob, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchCronJobs(namespace) } diff --git a/api/kubernetes/cli/cronjob_test.go b/api/kubernetes/cli/cronjob_test.go index af67c65ba..868b5a01b 100644 --- a/api/kubernetes/cli/cronjob_test.go +++ b/api/kubernetes/cli/cronjob_test.go @@ -18,7 +18,7 @@ func (kcl *KubeClient) TestFetchCronJobs(t *testing.T) { t.Run("admin client can fetch Cron Jobs from all namespaces", func(t *testing.T) { kcl.cli = kfake.NewSimpleClientset() kcl.instanceID = "test" - kcl.IsKubeAdmin = true + kcl.isKubeAdmin = true cronJobs, err := kcl.GetCronJobs("") if err != nil { @@ -31,8 +31,8 @@ func (kcl *KubeClient) TestFetchCronJobs(t *testing.T) { t.Run("non-admin client can fetch Cron Jobs from the default namespace only", func(t *testing.T) { kcl.cli = kfake.NewSimpleClientset() kcl.instanceID = "test" - kcl.IsKubeAdmin = false - kcl.NonAdminNamespaces = []string{"default"} + kcl.isKubeAdmin = false + kcl.SetClientNonAdminNamespaces([]string{"default"}) cronJobs, err := kcl.GetCronJobs("") if err != nil { diff --git a/api/kubernetes/cli/event.go b/api/kubernetes/cli/event.go index 35e6d0c4e..2c250fc16 100644 --- a/api/kubernetes/cli/event.go +++ b/api/kubernetes/cli/event.go @@ -12,7 +12,7 @@ import ( // If the user is a kube admin, it returns all events in the namespace // Otherwise, it returns only the events in the non-admin namespaces func (kcl *KubeClient) GetEvents(namespace string, resourceId string) ([]models.K8sEvent, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchAllEvents(namespace, resourceId) } @@ -22,7 +22,7 @@ func (kcl *KubeClient) GetEvents(namespace string, resourceId string) ([]models. // fetchEventsForNonAdmin returns all events in the given namespace and resource // It returns only the events in the non-admin namespaces func (kcl *KubeClient) fetchEventsForNonAdmin(namespace string, resourceId string) ([]models.K8sEvent, error) { - if len(kcl.NonAdminNamespaces) == 0 { + if len(kcl.GetClientNonAdminNamespaces()) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/event_test.go b/api/kubernetes/cli/event_test.go index 926928317..0d77bdad3 100644 --- a/api/kubernetes/cli/event_test.go +++ b/api/kubernetes/cli/event_test.go @@ -19,7 +19,7 @@ func TestGetEvents(t *testing.T) { kcl := &KubeClient{ cli: kfake.NewSimpleClientset(), instanceID: "instance", - IsKubeAdmin: true, + isKubeAdmin: true, } event := corev1.Event{ InvolvedObject: corev1.ObjectReference{UID: "resourceId"}, @@ -49,8 +49,8 @@ func TestGetEvents(t *testing.T) { kcl := &KubeClient{ cli: kfake.NewSimpleClientset(), instanceID: "instance", - IsKubeAdmin: false, - NonAdminNamespaces: []string{"nonAdmin"}, + isKubeAdmin: false, + nonAdminNamespaces: []string{"nonAdmin"}, } event := corev1.Event{ InvolvedObject: corev1.ObjectReference{UID: "resourceId"}, @@ -81,8 +81,8 @@ func TestGetEvents(t *testing.T) { kcl := &KubeClient{ cli: kfake.NewSimpleClientset(), instanceID: "instance", - IsKubeAdmin: false, - NonAdminNamespaces: []string{"nonAdmin"}, + isKubeAdmin: false, + nonAdminNamespaces: []string{"nonAdmin"}, } event := corev1.Event{ InvolvedObject: corev1.ObjectReference{UID: "resourceId"}, diff --git a/api/kubernetes/cli/ingress.go b/api/kubernetes/cli/ingress.go index a8f052562..9a8a343ad 100644 --- a/api/kubernetes/cli/ingress.go +++ b/api/kubernetes/cli/ingress.go @@ -87,17 +87,22 @@ func (kcl *KubeClient) GetIngress(namespace, ingressName string) (models.K8sIngr // GetIngresses gets all the ingresses for a given namespace in a k8s endpoint. func (kcl *KubeClient) GetIngresses(namespace string) ([]models.K8sIngressInfo, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchIngresses(namespace) } + return kcl.fetchIngressesForNonAdmin(namespace) } // fetchIngressesForNonAdmin gets all the ingresses for non-admin users in a k8s endpoint. func (kcl *KubeClient) fetchIngressesForNonAdmin(namespace string) ([]models.K8sIngressInfo, error) { - log.Debug().Msgf("Fetching ingresses for non-admin user: %v", kcl.NonAdminNamespaces) + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() - if len(kcl.NonAdminNamespaces) == 0 { + log.Debug(). + Strs("non_admin_namespaces", nonAdminNamespaces). + Msg("fetching ingresses for non-admin user") + + if len(nonAdminNamespaces) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/ingress_test.go b/api/kubernetes/cli/ingress_test.go new file mode 100644 index 000000000..0a186a3ee --- /dev/null +++ b/api/kubernetes/cli/ingress_test.go @@ -0,0 +1,15 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetIngresses(t *testing.T) { + kcl := &KubeClient{} + + ingresses, err := kcl.GetIngresses("default") + require.NoError(t, err) + require.Empty(t, ingresses) +} diff --git a/api/kubernetes/cli/job.go b/api/kubernetes/cli/job.go index 0a8c523a3..576882187 100644 --- a/api/kubernetes/cli/job.go +++ b/api/kubernetes/cli/job.go @@ -19,7 +19,7 @@ import ( // If the user is a kube admin, it returns all jobs in the namespace // Otherwise, it returns only the jobs in the non-admin namespaces func (kcl *KubeClient) GetJobs(namespace string, includeCronJobChildren bool) ([]models.K8sJob, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchJobs(namespace, includeCronJobChildren) } diff --git a/api/kubernetes/cli/job_test.go b/api/kubernetes/cli/job_test.go index b514dcc4c..425bbf6a7 100644 --- a/api/kubernetes/cli/job_test.go +++ b/api/kubernetes/cli/job_test.go @@ -21,7 +21,7 @@ func (kcl *KubeClient) TestFetchJobs(t *testing.T) { t.Run("admin client can fetch jobs from all namespaces", func(t *testing.T) { kcl.cli = kfake.NewSimpleClientset() kcl.instanceID = "test" - kcl.IsKubeAdmin = true + kcl.isKubeAdmin = true jobs, err := kcl.GetJobs("", false) if err != nil { @@ -34,8 +34,8 @@ func (kcl *KubeClient) TestFetchJobs(t *testing.T) { t.Run("non-admin client can fetch jobs from the default namespace only", func(t *testing.T) { kcl.cli = kfake.NewSimpleClientset() kcl.instanceID = "test" - kcl.IsKubeAdmin = false - kcl.NonAdminNamespaces = []string{"default"} + kcl.isKubeAdmin = false + kcl.SetClientNonAdminNamespaces([]string{"default"}) jobs, err := kcl.GetJobs("", false) if err != nil { diff --git a/api/kubernetes/cli/namespace.go b/api/kubernetes/cli/namespace.go index 560b91e75..2614d339d 100644 --- a/api/kubernetes/cli/namespace.go +++ b/api/kubernetes/cli/namespace.go @@ -40,9 +40,10 @@ func defaultSystemNamespaces() map[string]struct{} { // 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 { + if kcl.GetIsKubeAdmin() { return kcl.fetchNamespaces() } + return kcl.fetchNamespacesForNonAdmin() } @@ -52,7 +53,7 @@ func (kcl *KubeClient) fetchNamespacesForNonAdmin() (map[string]portainer.K8sNam Str("context", "fetchNamespacesForNonAdmin"). Msg("Fetching namespaces for non-admin user") - if len(kcl.NonAdminNamespaces) == 0 { + if len(kcl.GetClientNonAdminNamespaces()) == 0 { return nil, nil } @@ -142,6 +143,7 @@ func (kcl *KubeClient) CreateNamespace(info models.K8sNamespaceDetails) (*corev1 Str("context", "CreateNamespace"). Str("Namespace", info.Name). Msg("Failed to create the namespace") + return nil, err } @@ -157,7 +159,7 @@ func (kcl *KubeClient) CreateNamespace(info models.K8sNamespaceDetails) (*corev1 return namespace, nil } -// UpdateIngress updates an ingress in a given namespace in a k8s endpoint. +// UpdateNamespace updates a namespace in a k8s endpoint. func (kcl *KubeClient) UpdateNamespace(info models.K8sNamespaceDetails) (*corev1.Namespace, error) { portainerLabels := map[string]string{ namespaceNameLabel: stackutils.SanitizeLabel(info.Name), @@ -420,8 +422,10 @@ func (kcl *KubeClient) CombineNamespaceWithResourceQuota(namespace portainer.K8s // 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 { + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() + nonAdminNamespaceSet := make(map[string]struct{}, len(nonAdminNamespaces)) + + for _, namespace := range nonAdminNamespaces { if !isSystemDefaultNamespace(namespace) { nonAdminNamespaceSet[namespace] = struct{}{} } diff --git a/api/kubernetes/cli/namespace_test.go b/api/kubernetes/cli/namespace_test.go index d3d25a4d0..3f0198b1d 100644 --- a/api/kubernetes/cli/namespace_test.go +++ b/api/kubernetes/cli/namespace_test.go @@ -176,6 +176,7 @@ func Test_ToggleSystemState(t *testing.T) { expectedPolicies := map[string]portainer.K8sNamespaceAccessPolicy{ "ns2": {UserAccessPolicies: portainer.UserAccessPolicies{2: {RoleID: 0}}}, } + actualPolicies, err := kcl.GetNamespaceAccessPolicies() assert.NoError(t, err, "failed to fetch policies") assert.Equal(t, expectedPolicies, actualPolicies) diff --git a/api/kubernetes/cli/nodes_limits.go b/api/kubernetes/cli/nodes_limits.go index c3263c037..0b2a6415e 100644 --- a/api/kubernetes/cli/nodes_limits.go +++ b/api/kubernetes/cli/nodes_limits.go @@ -46,9 +46,9 @@ func (kcl *KubeClient) GetNodesLimits() (portainer.K8sNodesLimits, error) { // GetMaxResourceLimits gets the maximum CPU and Memory limits(unused resources) of all nodes in the current k8s environment(endpoint) connection, minus the accumulated resourcequotas for all namespaces except the one we're editing (skipNamespace) // if skipNamespace is set to "" then all namespaces are considered -func (client *KubeClient) GetMaxResourceLimits(skipNamespace string, overCommitEnabled bool, resourceOverCommitPercent int) (portainer.K8sNodeLimits, error) { +func (kcl *KubeClient) GetMaxResourceLimits(skipNamespace string, overCommitEnabled bool, resourceOverCommitPercent int) (portainer.K8sNodeLimits, error) { limits := portainer.K8sNodeLimits{} - nodes, err := client.cli.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) + nodes, err := kcl.cli.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err != nil { return limits, err } @@ -62,7 +62,7 @@ func (client *KubeClient) GetMaxResourceLimits(skipNamespace string, overCommitE limits.Memory = memory / 1000000 // B to MB if !overCommitEnabled { - namespaces, err := client.cli.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) + namespaces, err := kcl.cli.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) if err != nil { return limits, err } @@ -77,7 +77,7 @@ func (client *KubeClient) GetMaxResourceLimits(skipNamespace string, overCommitE } // minus accumulated resourcequotas for all namespaces except the one we're editing - resourceQuota, err := client.cli.CoreV1().ResourceQuotas(namespace.Name).List(context.TODO(), metav1.ListOptions{}) + resourceQuota, err := kcl.cli.CoreV1().ResourceQuotas(namespace.Name).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Debug().Msgf("error getting resourcequota for namespace %s: %s", namespace.Name, err) continue // skip it diff --git a/api/kubernetes/cli/pod_test.go b/api/kubernetes/cli/pod_test.go index f363ac1b6..58b143aad 100644 --- a/api/kubernetes/cli/pod_test.go +++ b/api/kubernetes/cli/pod_test.go @@ -59,6 +59,7 @@ func Test_waitForPodStatus(t *testing.T) { ctx, cancelFunc := context.WithTimeout(context.TODO(), 0*time.Second) defer cancelFunc() + err = k.waitForPodStatus(ctx, v1.PodRunning, podSpec) if !errors.Is(err, context.DeadlineExceeded) { t.Errorf("waitForPodStatus should throw deadline exceeded error; err=%s", err) diff --git a/api/kubernetes/cli/resource_quota.go b/api/kubernetes/cli/resource_quota.go index dec40e014..5edb2b628 100644 --- a/api/kubernetes/cli/resource_quota.go +++ b/api/kubernetes/cli/resource_quota.go @@ -15,18 +15,23 @@ import ( // 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 { + if kcl.GetIsKubeAdmin() { return kcl.fetchResourceQuotas(namespace) } + return kcl.fetchResourceQuotasForNonAdmin(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) { - log.Debug().Msgf("Fetching resource quotas for non-admin user: %v", kcl.NonAdminNamespaces) + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() - if len(kcl.NonAdminNamespaces) == 0 { + log.Debug(). + Strs("non_admin_namespaces", nonAdminNamespaces). + Msg("fetching resource quotas for non-admin user") + + if len(nonAdminNamespaces) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/role.go b/api/kubernetes/cli/role.go index c45e2c299..322e9987a 100644 --- a/api/kubernetes/cli/role.go +++ b/api/kubernetes/cli/role.go @@ -15,7 +15,7 @@ import ( // GetRoles gets all the roles for either at the cluster level or a given namespace in a k8s endpoint. // It returns a list of K8sRole objects. func (kcl *KubeClient) GetRoles(namespace string) ([]models.K8sRole, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchRoles(namespace) } diff --git a/api/kubernetes/cli/role_binding.go b/api/kubernetes/cli/role_binding.go index 7775748e2..d85d54436 100644 --- a/api/kubernetes/cli/role_binding.go +++ b/api/kubernetes/cli/role_binding.go @@ -15,7 +15,7 @@ import ( // GetRoleBindings gets all the roleBindings for either at the cluster level or a given namespace in a k8s endpoint. // It returns a list of K8sRoleBinding objects. func (kcl *KubeClient) GetRoleBindings(namespace string) ([]models.K8sRoleBinding, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchRoleBindings(namespace) } diff --git a/api/kubernetes/cli/secret.go b/api/kubernetes/cli/secret.go index 5bcd386cb..0845ca027 100644 --- a/api/kubernetes/cli/secret.go +++ b/api/kubernetes/cli/secret.go @@ -23,18 +23,23 @@ const ( // if the user is an admin, all secrets in the current k8s environment(endpoint) are fetched using the getSecrets function. // otherwise, namespaces the non-admin user has access to will be used to filter the secrets based on the allowed namespaces. func (kcl *KubeClient) GetSecrets(namespace string) ([]models.K8sSecret, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.getSecrets(namespace) } + return kcl.getSecretsForNonAdmin(namespace) } // getSecretsForNonAdmin fetches the secrets in the namespaces the user has access to. // This function is called when the user is not an admin. func (kcl *KubeClient) getSecretsForNonAdmin(namespace string) ([]models.K8sSecret, error) { - log.Debug().Msgf("Fetching secrets for non-admin user: %v", kcl.NonAdminNamespaces) + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() - if len(kcl.NonAdminNamespaces) == 0 { + log.Debug(). + Strs("non_admin_namespaces", nonAdminNamespaces). + Msg("fetching secrets for non-admin user") + + if len(nonAdminNamespaces) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/service.go b/api/kubernetes/cli/service.go index 3f0543735..ce48ec071 100644 --- a/api/kubernetes/cli/service.go +++ b/api/kubernetes/cli/service.go @@ -15,9 +15,10 @@ import ( // GetServices gets all the services for either at the cluster level or a given namespace in a k8s endpoint. // It returns a list of K8sServiceInfo objects. func (kcl *KubeClient) GetServices(namespace string) ([]models.K8sServiceInfo, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchServices(namespace) } + return kcl.fetchServicesForNonAdmin(namespace) } @@ -25,9 +26,13 @@ func (kcl *KubeClient) GetServices(namespace string) ([]models.K8sServiceInfo, e // the namespace will be coming from NonAdminNamespaces as non-admin users are restricted to certain namespaces. // it returns a list of K8sServiceInfo objects. func (kcl *KubeClient) fetchServicesForNonAdmin(namespace string) ([]models.K8sServiceInfo, error) { - log.Debug().Msgf("Fetching services for non-admin user: %v", kcl.NonAdminNamespaces) + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() - if len(kcl.NonAdminNamespaces) == 0 { + log.Debug(). + Strs("non_admin_namespaces", nonAdminNamespaces). + Msg("fetching services for non-admin user") + + if len(nonAdminNamespaces) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/service_account.go b/api/kubernetes/cli/service_account.go index af674d794..4711e9d34 100644 --- a/api/kubernetes/cli/service_account.go +++ b/api/kubernetes/cli/service_account.go @@ -16,7 +16,7 @@ import ( // GetServiceAccounts gets all the service accounts for either at the cluster level or a given namespace in a k8s endpoint. // It returns a list of K8sServiceAccount objects. func (kcl *KubeClient) GetServiceAccounts(namespace string) ([]models.K8sServiceAccount, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchServiceAccounts(namespace) } diff --git a/api/kubernetes/cli/service_test.go b/api/kubernetes/cli/service_test.go new file mode 100644 index 000000000..c7192ff1c --- /dev/null +++ b/api/kubernetes/cli/service_test.go @@ -0,0 +1,15 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetServices(t *testing.T) { + kcl := &KubeClient{} + + services, err := kcl.GetServices("default") + require.NoError(t, err) + require.Empty(t, services) +} diff --git a/api/kubernetes/cli/volumes.go b/api/kubernetes/cli/volumes.go index 36b04dc89..60db03dfb 100644 --- a/api/kubernetes/cli/volumes.go +++ b/api/kubernetes/cli/volumes.go @@ -18,9 +18,10 @@ import ( // If the user is not an admin, it fetches the volumes in the namespaces the user has access to. // It returns a list of K8sVolumeInfo. func (kcl *KubeClient) GetVolumes(namespace string) ([]models.K8sVolumeInfo, error) { - if kcl.IsKubeAdmin { + if kcl.GetIsKubeAdmin() { return kcl.fetchVolumes(namespace) } + return kcl.fetchVolumesForNonAdmin(namespace) } @@ -48,9 +49,13 @@ func (kcl *KubeClient) GetVolume(namespace, volumeName string) (*models.K8sVolum // This function is called when the user is not an admin. // It fetches all the persistent volume claims, persistent volumes and storage classes in the namespaces the user has access to. func (kcl *KubeClient) fetchVolumesForNonAdmin(namespace string) ([]models.K8sVolumeInfo, error) { - log.Debug().Msgf("Fetching volumes for non-admin user: %v", kcl.NonAdminNamespaces) + nonAdminNamespaces := kcl.GetClientNonAdminNamespaces() - if len(kcl.NonAdminNamespaces) == 0 { + log.Debug(). + Strs("non_admin_namespaces", nonAdminNamespaces). + Msg("fetching volumes for non-admin user") + + if len(nonAdminNamespaces) == 0 { return nil, nil } diff --git a/api/kubernetes/cli/volumes_test.go b/api/kubernetes/cli/volumes_test.go new file mode 100644 index 000000000..9a37e487c --- /dev/null +++ b/api/kubernetes/cli/volumes_test.go @@ -0,0 +1,15 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetVolumes(t *testing.T) { + kcl := &KubeClient{} + + volumes, err := kcl.GetVolumes("default") + require.NoError(t, err) + require.Empty(t, volumes) +}