fix: cluster list granted to users is incorrect

This commit is contained in:
hongming
2022-05-20 17:12:55 +08:00
parent 32ac94a7e5
commit 382be8b16b
11 changed files with 176 additions and 142 deletions

View File

@@ -488,9 +488,10 @@ func addAllControllers(mgr manager.Manager, client k8s.Client, informerFactory i
if cmOptions.MultiClusterOptions.Enable {
clusterController := cluster.NewClusterController(
client.Kubernetes(),
client.KubeSphere(),
client.Config(),
kubesphereInformer.Cluster().V1alpha1().Clusters(),
client.KubeSphere().ClusterV1alpha1().Clusters(),
kubesphereInformer.Iam().V1alpha2().Users().Lister(),
cmOptions.MultiClusterOptions.ClusterControllerResyncPeriod,
cmOptions.MultiClusterOptions.HostClusterName,
)

View File

@@ -26,31 +26,25 @@ import (
"sync"
"time"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
"kubesphere.io/kubesphere/pkg/utils/clusterclient"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/discovery"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry"
"github.com/emicklei/go-restful"
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
unionauth "k8s.io/apiserver/pkg/authentication/request/union"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/client-go/discovery"
"k8s.io/client-go/util/retry"
"k8s.io/klog"
runtimecache "sigs.k8s.io/controller-runtime/pkg/cache"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
clusterv1alpha1 "kubesphere.io/api/cluster/v1alpha1"
iamv1alpha2 "kubesphere.io/api/iam/v1alpha2"
notificationv2beta1 "kubesphere.io/api/notification/v2beta1"
tenantv1alpha1 "kubesphere.io/api/tenant/v1alpha1"
typesv1beta1 "kubesphere.io/api/types/v1beta1"
runtimecache "sigs.k8s.io/controller-runtime/pkg/cache"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
audit "kubesphere.io/kubesphere/pkg/apiserver/auditing"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic"
@@ -101,6 +95,7 @@ import (
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/group"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/loginrecord"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/user"
"kubesphere.io/kubesphere/pkg/simple/client/alerting"
@@ -113,6 +108,7 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
"kubesphere.io/kubesphere/pkg/utils/clusterclient"
"kubesphere.io/kubesphere/pkg/utils/iputil"
"kubesphere.io/kubesphere/pkg/utils/metrics"
)
@@ -238,9 +234,9 @@ func (s *APIServer) installKubeSphereAPIs(stopCh <-chan struct{}) {
urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory,
s.KubernetesClient.Master()))
urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.InformerFactory, s.KubernetesClient.Kubernetes(),
s.KubernetesClient.KubeSphere(), s.EventsClient, s.LoggingClient, s.AuditingClient, amOperator, rbacAuthorizer, s.MonitoringClient, s.RuntimeCache, s.Config.MeteringOptions, s.OpenpitrixClient))
s.KubernetesClient.KubeSphere(), s.EventsClient, s.LoggingClient, s.AuditingClient, amOperator, imOperator, rbacAuthorizer, s.MonitoringClient, s.RuntimeCache, s.Config.MeteringOptions, s.OpenpitrixClient))
urlruntime.Must(tenantv1alpha3.AddToContainer(s.container, s.InformerFactory, s.KubernetesClient.Kubernetes(),
s.KubernetesClient.KubeSphere(), s.EventsClient, s.LoggingClient, s.AuditingClient, amOperator, rbacAuthorizer, s.MonitoringClient, s.RuntimeCache, s.Config.MeteringOptions, s.OpenpitrixClient))
s.KubernetesClient.KubeSphere(), s.EventsClient, s.LoggingClient, s.AuditingClient, amOperator, imOperator, rbacAuthorizer, s.MonitoringClient, s.RuntimeCache, s.Config.MeteringOptions, s.OpenpitrixClient))
urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), rbacAuthorizer, s.KubernetesClient.Config(), s.Config.TerminalOptions))
urlruntime.Must(clusterkapisv1alpha1.AddToContainer(s.container,
s.KubernetesClient.KubeSphere(),

View File

@@ -25,6 +25,7 @@ import (
"fmt"
"net/http"
"reflect"
"strings"
"time"
"gopkg.in/yaml.v2"
@@ -48,11 +49,13 @@ import (
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
clusterv1alpha1 "kubesphere.io/api/cluster/v1alpha1"
iamv1alpha2 "kubesphere.io/api/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/config"
clusterclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
clusterinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/cluster/v1alpha1"
clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/simple/client/multicluster"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
@@ -126,12 +129,13 @@ type clusterController struct {
eventRecorder record.EventRecorder
// build this only for host cluster
client kubernetes.Interface
k8sClient kubernetes.Interface
hostConfig *rest.Config
clusterClient clusterclient.ClusterInterface
ksClient kubesphere.Interface
clusterLister clusterlister.ClusterLister
userLister iamv1alpha2listers.UserLister
clusterHasSynced cache.InformerSynced
queue workqueue.RateLimitingInterface
@@ -144,10 +148,11 @@ type clusterController struct {
}
func NewClusterController(
client kubernetes.Interface,
k8sClient kubernetes.Interface,
ksClient kubesphere.Interface,
config *rest.Config,
clusterInformer clusterinformer.ClusterInformer,
clusterClient clusterclient.ClusterInterface,
userLister iamv1alpha2listers.UserLister,
resyncPeriod time.Duration,
hostClusterName string,
) *clusterController {
@@ -156,19 +161,20 @@ func NewClusterController(
broadcaster.StartLogging(func(format string, args ...interface{}) {
klog.Info(fmt.Sprintf(format, args))
})
broadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: client.CoreV1().Events("")})
broadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")})
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cluster-controller"})
c := &clusterController{
eventBroadcaster: broadcaster,
eventRecorder: recorder,
client: client,
k8sClient: k8sClient,
ksClient: ksClient,
hostConfig: config,
clusterClient: clusterClient,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "cluster"),
workerLoopPeriod: time.Second,
resyncPeriod: resyncPeriod,
hostClusterName: hostClusterName,
userLister: userLister,
}
c.clusterLister = clusterInformer.Lister()
c.clusterHasSynced = clusterInformer.Informer().HasSynced
@@ -213,7 +219,7 @@ func (c *clusterController) Run(workers int, stopCh <-chan struct{}) error {
klog.Errorf("Error create host cluster, error %v", err)
}
if err := c.probeClusters(); err != nil {
if err := c.resyncClusters(); err != nil {
klog.Errorf("failed to reconcile cluster ready status, err: %v", err)
}
}, c.resyncPeriod, stopCh)
@@ -256,7 +262,7 @@ func (c *clusterController) reconcileHostCluster() error {
if len(clusters) == 0 {
hostCluster.Spec.Connection.KubeConfig = hostKubeConfig
hostCluster.Name = c.hostClusterName
_, err = c.clusterClient.Create(context.TODO(), hostCluster, metav1.CreateOptions{})
_, err = c.ksClient.ClusterV1alpha1().Clusters().Create(context.TODO(), hostCluster, metav1.CreateOptions{})
return err
} else if len(clusters) > 1 {
return fmt.Errorf("there MUST not be more than one host clusters, while there are %d", len(clusters))
@@ -280,18 +286,20 @@ func (c *clusterController) reconcileHostCluster() error {
}
// update host cluster config
_, err = c.clusterClient.Update(context.TODO(), cluster, metav1.UpdateOptions{})
_, err = c.ksClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})
return err
}
func (c *clusterController) probeClusters() error {
func (c *clusterController) resyncClusters() error {
clusters, err := c.clusterLister.List(labels.Everything())
if err != nil {
return err
}
for _, cluster := range clusters {
c.syncCluster(cluster.Name)
if err = c.syncCluster(cluster.Name); err != nil {
klog.Warningf("failed to sync cluster %s: %s", cluster.Name, err)
}
}
return nil
}
@@ -328,7 +336,7 @@ func (c *clusterController) syncCluster(key string) error {
// registering our finalizer.
if !sets.NewString(cluster.ObjectMeta.Finalizers...).Has(clusterv1alpha1.Finalizer) {
cluster.ObjectMeta.Finalizers = append(cluster.ObjectMeta.Finalizers, clusterv1alpha1.Finalizer)
if cluster, err = c.clusterClient.Update(context.TODO(), cluster, metav1.UpdateOptions{}); err != nil {
if cluster, err = c.ksClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{}); err != nil {
return err
}
}
@@ -338,17 +346,21 @@ func (c *clusterController) syncCluster(key string) error {
// need to unJoin federation first, before there are
// some cleanup work to do in member cluster which depends
// agent to proxy traffic
err = c.unJoinFederation(nil, name)
if err != nil {
if err = c.unJoinFederation(nil, name); err != nil {
klog.Errorf("Failed to unjoin federation for cluster %s, error %v", name, err)
return err
}
// cleanup after cluster has been deleted
if err := c.syncClusterMembers(nil, cluster); err != nil {
klog.Errorf("Failed to sync cluster members for %s: %v", name, err)
return err
}
// remove our cluster finalizer
finalizers := sets.NewString(cluster.ObjectMeta.Finalizers...)
finalizers.Delete(clusterv1alpha1.Finalizer)
cluster.ObjectMeta.Finalizers = finalizers.List()
if _, err = c.clusterClient.Update(context.TODO(), cluster, metav1.UpdateOptions{}); err != nil {
if _, err = c.ksClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{}); err != nil {
return err
}
}
@@ -407,7 +419,7 @@ func (c *clusterController) syncCluster(key string) error {
}
c.updateClusterCondition(cluster, federationNotReadyCondition)
_, err = c.clusterClient.Update(context.TODO(), cluster, metav1.UpdateOptions{})
_, err = c.ksClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})
if err != nil {
klog.Errorf("Failed to update cluster status, %#v", err)
}
@@ -496,8 +508,8 @@ func (c *clusterController) syncCluster(key string) error {
return err
}
if !reflect.DeepEqual(oldCluster, cluster) {
_, err = c.clusterClient.Update(context.TODO(), cluster, metav1.UpdateOptions{})
if !reflect.DeepEqual(oldCluster.Status, cluster.Status) {
_, err = c.ksClient.ClusterV1alpha1().Clusters().Update(context.TODO(), cluster, metav1.UpdateOptions{})
if err != nil {
klog.Errorf("Failed to update cluster status, %#v", err)
return err
@@ -508,6 +520,10 @@ func (c *clusterController) syncCluster(key string) error {
return err
}
if err = c.syncClusterMembers(clusterClient, cluster); err != nil {
return fmt.Errorf("failed to sync cluster membership for %s: %s", cluster.Name, err)
}
return nil
}
@@ -541,7 +557,7 @@ func (c *clusterController) setClusterNameInConfigMap(client kubernetes.Interfac
}
func (c *clusterController) checkIfClusterIsHostCluster(memberClusterNodes *v1.NodeList) bool {
hostNodes, err := c.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
hostNodes, err := c.k8sClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return false
}
@@ -786,3 +802,55 @@ func (c *clusterController) updateKubeConfigExpirationDateCondition(cluster *clu
})
return nil
}
// syncClusterMembers Sync granted clusters for users periodically
func (c *clusterController) syncClusterMembers(clusterClient *kubernetes.Clientset, cluster *clusterv1alpha1.Cluster) error {
users, err := c.userLister.List(labels.Everything())
if err != nil {
return fmt.Errorf("failed to list users: %s", err)
}
grantedUsers := sets.NewString()
clusterName := cluster.Name
if cluster.DeletionTimestamp.IsZero() {
list, err := clusterClient.RbacV1().ClusterRoleBindings().List(context.Background(),
metav1.ListOptions{LabelSelector: iamv1alpha2.UserReferenceLabel})
if err != nil {
return fmt.Errorf("failed to list clusterrolebindings: %s", err)
}
for _, clusterRoleBinding := range list.Items {
for _, sub := range clusterRoleBinding.Subjects {
if sub.Kind == iamv1alpha2.ResourceKindUser {
grantedUsers.Insert(sub.Name)
}
}
}
}
for _, user := range users {
user = user.DeepCopy()
grantedClustersAnnotation := user.Annotations[iamv1alpha2.GrantedClustersAnnotation]
var grantedClusters sets.String
if len(grantedClustersAnnotation) > 0 {
grantedClusters = sets.NewString(strings.Split(grantedClustersAnnotation, ",")...)
} else {
grantedClusters = sets.NewString()
}
if grantedUsers.Has(user.Name) && !grantedClusters.Has(clusterName) {
grantedClusters.Insert(clusterName)
} else if !grantedUsers.Has(user.Name) && grantedClusters.Has(clusterName) {
grantedClusters.Delete(clusterName)
}
grantedClustersAnnotation = strings.Join(grantedClusters.List(), ",")
if user.Annotations[iamv1alpha2.GrantedClustersAnnotation] != grantedClustersAnnotation {
if user.Annotations == nil {
user.Annotations = make(map[string]string, 0)
}
user.Annotations[iamv1alpha2.GrantedClustersAnnotation] = grantedClustersAnnotation
if _, err := c.ksClient.IamV1alpha2().Users().Update(context.Background(), user, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("failed to update user %s: %s", user.Name, err)
}
}
}
return nil
}

View File

@@ -28,8 +28,6 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
quotav1alpha2 "kubesphere.io/api/quota/v1alpha2"
tenantv1alpha2 "kubesphere.io/api/tenant/v1alpha2"
@@ -43,6 +41,8 @@ import (
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
"kubesphere.io/kubesphere/pkg/models/tenant"
servererr "kubesphere.io/kubesphere/pkg/server/errors"
@@ -60,7 +60,7 @@ type tenantHandler struct {
func NewTenantHandler(factory informers.InformerFactory, k8sclient kubernetes.Interface, ksclient kubesphere.Interface,
evtsClient events.Client, loggingClient logging.Client, auditingclient auditing.Client,
am am.AccessManagementInterface, authorizer authorizer.Authorizer,
am am.AccessManagementInterface, im im.IdentityManagementInterface, authorizer authorizer.Authorizer,
monitoringclient monitoringclient.Interface, resourceGetter *resourcev1alpha3.ResourceGetter,
meteringOptions *meteringclient.Options, opClient openpitrix.Interface) *tenantHandler {
@@ -69,7 +69,7 @@ func NewTenantHandler(factory informers.InformerFactory, k8sclient kubernetes.In
}
return &tenantHandler{
tenant: tenant.New(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, authorizer, monitoringclient, resourceGetter, opClient),
tenant: tenant.New(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, im, authorizer, monitoringclient, resourceGetter, opClient),
meteringOptions: meteringOptions,
}
}
@@ -557,8 +557,8 @@ func (h *tenantHandler) ListClusters(r *restful.Request, response *restful.Respo
return
}
result, err := h.tenant.ListClusters(user)
queryParam := query.ParseQueryParameter(r)
result, err := h.tenant.ListClusters(user, queryParam)
if err != nil {
klog.Error(err)
if errors.IsNotFound(err) {

View File

@@ -19,8 +19,6 @@ package v1alpha2
import (
"net/http"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
"github.com/emicklei/go-restful"
restfulspec "github.com/emicklei/go-restful-openapi"
corev1 "k8s.io/api/core/v1"
@@ -42,8 +40,10 @@ import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/models/metering"
"kubesphere.io/kubesphere/pkg/models/monitoring"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/auditing"
@@ -65,12 +65,12 @@ func Resource(resource string) schema.GroupResource {
func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8sclient kubernetes.Interface,
ksclient kubesphere.Interface, evtsClient events.Client, loggingClient logging.Client,
auditingclient auditing.Client, am am.AccessManagementInterface, authorizer authorizer.Authorizer,
auditingclient auditing.Client, am am.AccessManagementInterface, im im.IdentityManagementInterface, authorizer authorizer.Authorizer,
monitoringclient monitoringclient.Interface, cache cache.Cache, meteringOptions *meteringclient.Options, opClient openpitrix.Interface) error {
mimePatch := []string{restful.MIME_JSON, runtime.MimeMergePatchJson, runtime.MimeJsonPatchJson}
ws := runtime.NewWebService(GroupVersion)
handler := NewTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, authorizer, monitoringclient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opClient)
handler := NewTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, im, authorizer, monitoringclient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opClient)
ws.Route(ws.GET("/clusters").
To(handler.ListClusters).

View File

@@ -24,8 +24,6 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/query"
@@ -33,6 +31,8 @@ import (
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
"kubesphere.io/kubesphere/pkg/models/tenant"
"kubesphere.io/kubesphere/pkg/simple/client/auditing"
@@ -49,7 +49,7 @@ type tenantHandler struct {
func newTenantHandler(factory informers.InformerFactory, k8sclient kubernetes.Interface, ksclient kubesphere.Interface,
evtsClient events.Client, loggingClient logging.Client, auditingclient auditing.Client,
am am.AccessManagementInterface, authorizer authorizer.Authorizer,
am am.AccessManagementInterface, im im.IdentityManagementInterface, authorizer authorizer.Authorizer,
monitoringclient monitoringclient.Interface, resourceGetter *resourcev1alpha3.ResourceGetter,
meteringOptions *meteringclient.Options, opClient openpitrix.Interface) *tenantHandler {
@@ -58,7 +58,7 @@ func newTenantHandler(factory informers.InformerFactory, k8sclient kubernetes.In
}
return &tenantHandler{
tenant: tenant.New(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, authorizer, monitoringclient, resourceGetter, opClient),
tenant: tenant.New(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, im, authorizer, monitoringclient, resourceGetter, opClient),
meteringOptions: meteringOptions,
}
}

View File

@@ -19,8 +19,6 @@ package v1alpha3
import (
"net/http"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
"github.com/emicklei/go-restful"
restfulspec "github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -39,6 +37,8 @@ import (
"kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/models/openpitrix"
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/auditing"
@@ -60,14 +60,13 @@ func Resource(resource string) schema.GroupResource {
func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8sclient kubernetes.Interface,
ksclient kubesphere.Interface, evtsClient events.Client, loggingClient logging.Client,
auditingclient auditing.Client, am am.AccessManagementInterface, authorizer authorizer.Authorizer,
monitoringclient monitoringclient.Interface, cache cache.Cache, meteringOptions *meteringclient.Options,
opClient openpitrix.Interface) error {
auditingclient auditing.Client, am am.AccessManagementInterface, im im.IdentityManagementInterface, authorizer authorizer.Authorizer,
monitoringclient monitoringclient.Interface, cache cache.Cache, meteringOptions *meteringclient.Options, opClient openpitrix.Interface) error {
mimePatch := []string{restful.MIME_JSON, runtime.MimeMergePatchJson, runtime.MimeJsonPatchJson}
ws := runtime.NewWebService(GroupVersion)
v1alpha2Handler := v1alpha2.NewTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, authorizer, monitoringclient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opClient)
handler := newTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, authorizer, monitoringclient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opClient)
v1alpha2Handler := v1alpha2.NewTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, im, authorizer, monitoringclient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opClient)
handler := newTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient, am, im, authorizer, monitoringclient, resourcev1alpha3.NewResourceGetter(factory, cache), meteringOptions, opClient)
ws.Route(ws.POST("/workspacetemplates").
To(v1alpha2Handler.CreateWorkspaceTemplate).

View File

@@ -20,6 +20,9 @@ import (
"sort"
"strings"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/klog"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -150,33 +153,13 @@ func DefaultObjectMetaFilter(item metav1.ObjectMeta, filter query.Filter) bool {
}
}
func labelMatch(labels map[string]string, filter string) bool {
fields := strings.SplitN(filter, "=", 2)
var key, value string
var opposite bool
if len(fields) == 2 {
key = fields[0]
if strings.HasSuffix(key, "!") {
key = strings.TrimSuffix(key, "!")
opposite = true
}
value = fields[1]
} else {
key = fields[0]
value = "*"
func labelMatch(m map[string]string, filter string) bool {
labelSelector, err := labels.Parse(filter)
if err != nil {
klog.Warningf("invalid labelSelector %s: %s", filter, err)
return false
}
for k, v := range labels {
if opposite {
if (k == key) && v != value {
return true
}
} else {
if (k == key) && (value == "*" || v == value) {
return true
}
}
}
return false
return labelSelector.Matches(labels.Set(m))
}
func objectsToInterfaces(objs []runtime.Object) []interface{} {

View File

@@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
@@ -40,6 +41,8 @@ import (
tenantv1alpha2 "kubesphere.io/api/tenant/v1alpha2"
typesv1beta1 "kubesphere.io/api/types/v1beta1"
iamv1alpha2 "kubesphere.io/api/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/api"
auditingv1alpha1 "kubesphere.io/kubesphere/pkg/api/auditing/v1alpha1"
eventsv1alpha1 "kubesphere.io/kubesphere/pkg/api/events/v1alpha1"
@@ -53,6 +56,7 @@ import (
"kubesphere.io/kubesphere/pkg/models/auditing"
"kubesphere.io/kubesphere/pkg/models/events"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
"kubesphere.io/kubesphere/pkg/models/logging"
"kubesphere.io/kubesphere/pkg/models/metering"
"kubesphere.io/kubesphere/pkg/models/monitoring"
@@ -92,7 +96,7 @@ type Interface interface {
DeleteNamespace(workspace, namespace string) error
UpdateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
PatchNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
ListClusters(info user.Info) (*api.ListResult, error)
ListClusters(info user.Info, queryParam *query.Query) (*api.ListResult, error)
Metering(user user.Info, queryParam *meteringv1alpha1.Query, priceInfo meteringclient.PriceInfo) (monitoring.Metrics, error)
MeteringHierarchy(user user.Info, queryParam *meteringv1alpha1.Query, priceInfo meteringclient.PriceInfo) (metering.ResourceStatistic, error)
CreateWorkspaceResourceQuota(workspace string, resourceQuota *quotav1alpha2.ResourceQuota) (*quotav1alpha2.ResourceQuota, error)
@@ -103,6 +107,7 @@ type Interface interface {
type tenantOperator struct {
am am.AccessManagementInterface
im im.IdentityManagementInterface
authorizer authorizer.Authorizer
k8sclient kubernetes.Interface
ksclient kubesphere.Interface
@@ -114,9 +119,10 @@ type tenantOperator struct {
opRelease openpitrix.ReleaseInterface
}
func New(informers informers.InformerFactory, k8sclient kubernetes.Interface, ksclient kubesphere.Interface, evtsClient eventsclient.Client, loggingClient loggingclient.Client, auditingclient auditingclient.Client, am am.AccessManagementInterface, authorizer authorizer.Authorizer, monitoringclient monitoringclient.Interface, resourceGetter *resourcev1alpha3.ResourceGetter, opClient openpitrix.Interface) Interface {
func New(informers informers.InformerFactory, k8sclient kubernetes.Interface, ksclient kubesphere.Interface, evtsClient eventsclient.Client, loggingClient loggingclient.Client, auditingclient auditingclient.Client, am am.AccessManagementInterface, im im.IdentityManagementInterface, authorizer authorizer.Authorizer, monitoringclient monitoringclient.Interface, resourceGetter *resourcev1alpha3.ResourceGetter, opClient openpitrix.Interface) Interface {
return &tenantOperator{
am: am,
im: im,
authorizer: authorizer,
resourceGetter: resourcesv1alpha3.NewResourceGetter(informers, nil),
k8sclient: k8sclient,
@@ -498,7 +504,7 @@ func (t *tenantOperator) ListWorkspaceClusters(workspaceName string) (*api.ListR
for _, cluster := range workspace.Spec.Placement.Clusters {
obj, err := t.resourceGetter.Get(clusterv1alpha1.ResourcesPluralCluster, "", cluster.Name)
if err != nil {
klog.Error(err)
klog.Warning(err)
if errors.IsNotFound(err) {
continue
}
@@ -527,89 +533,69 @@ func (t *tenantOperator) ListWorkspaceClusters(workspaceName string) (*api.ListR
return &api.ListResult{Items: []interface{}{}, TotalItems: 0}, nil
}
func (t *tenantOperator) ListClusters(user user.Info) (*api.ListResult, error) {
func (t *tenantOperator) ListClusters(user user.Info, queryParam *query.Query) (*api.ListResult, error) {
listClustersInGlobalScope := authorizer.AttributesRecord{
User: user,
Verb: "list",
APIGroup: "cluster.kubesphere.io",
Resource: "clusters",
ResourceScope: request.GlobalScope,
ResourceRequest: true,
}
allowedListClustersInGlobalScope, _, err := t.authorizer.Authorize(listClustersInGlobalScope)
if err != nil {
klog.Error(err)
return nil, err
return nil, fmt.Errorf("failed to authorize: %s", err)
}
listWorkspacesInGlobalScope := authorizer.AttributesRecord{
User: user,
Verb: "list",
Resource: "workspaces",
ResourceScope: request.GlobalScope,
ResourceRequest: true,
if allowedListClustersInGlobalScope == authorizer.DecisionAllow {
return t.resourceGetter.List(clusterv1alpha1.ResourcesPluralCluster, "", queryParam)
}
allowedListWorkspacesInGlobalScope, _, err := t.authorizer.Authorize(listWorkspacesInGlobalScope)
userDetail, err := t.im.DescribeUser(user.GetName())
if err != nil {
klog.Error(err)
return nil, err
return nil, fmt.Errorf("failed to describe user: %s", err)
}
if allowedListClustersInGlobalScope == authorizer.DecisionAllow ||
allowedListWorkspacesInGlobalScope == authorizer.DecisionAllow {
result, err := t.resourceGetter.List(clusterv1alpha1.ResourcesPluralCluster, "", query.New())
grantedClustersAnnotation := userDetail.Annotations[iamv1alpha2.GrantedClustersAnnotation]
var grantedClusters sets.String
if len(grantedClustersAnnotation) > 0 {
grantedClusters = sets.NewString(strings.Split(grantedClustersAnnotation, ",")...)
} else {
grantedClusters = sets.NewString()
}
var clusters []*clusterv1alpha1.Cluster
for _, grantedCluster := range grantedClusters.List() {
obj, err := t.resourceGetter.Get(clusterv1alpha1.ResourcesPluralCluster, "", grantedCluster)
if err != nil {
klog.Error(err)
return nil, err
}
return result, nil
}
workspaceRoleBindings, err := t.am.ListWorkspaceRoleBindings(user.GetName(), user.GetGroups(), "")
if err != nil {
klog.Error(err)
return nil, err
}
clusters := map[string]*clusterv1alpha1.Cluster{}
for _, roleBinding := range workspaceRoleBindings {
workspaceName := roleBinding.Labels[tenantv1alpha1.WorkspaceLabel]
workspace, err := t.DescribeWorkspaceTemplate(workspaceName)
if err != nil {
klog.Error(err)
return nil, err
}
for _, grantedCluster := range workspace.Spec.Placement.Clusters {
// skip if cluster exist
if clusters[grantedCluster.Name] != nil {
if errors.IsNotFound(err) {
continue
}
obj, err := t.resourceGetter.Get(clusterv1alpha1.ResourcesPluralCluster, "", grantedCluster.Name)
if err != nil {
klog.Error(err)
if errors.IsNotFound(err) {
continue
}
return nil, err
}
cluster := obj.(*clusterv1alpha1.Cluster)
clusters[cluster.Name] = cluster
return nil, fmt.Errorf("failed to fetch cluster: %s", err)
}
cluster := obj.(*clusterv1alpha1.Cluster)
clusters = append(clusters, cluster)
}
items := make([]interface{}, 0)
items := make([]runtime.Object, 0)
for _, cluster := range clusters {
items = append(items, cluster)
}
return &api.ListResult{Items: items, TotalItems: len(items)}, nil
// apply additional labelSelector
if queryParam.LabelSelector != "" {
queryParam.Filters[query.FieldLabel] = query.Value(queryParam.LabelSelector)
}
// use default pagination search logic
result := resources.DefaultList(items, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool {
return resources.DefaultObjectMetaCompare(left.(*clusterv1alpha1.Cluster).ObjectMeta, right.(*clusterv1alpha1.Cluster).ObjectMeta, field)
}, func(workspace runtime.Object, filter query.Filter) bool {
return resources.DefaultObjectMetaFilter(workspace.(*clusterv1alpha1.Cluster).ObjectMeta, filter)
})
return result, nil
}
func (t *tenantOperator) DeleteWorkspaceTemplate(workspace string, opts metav1.DeleteOptions) error {

View File

@@ -544,5 +544,5 @@ func prepare() Interface {
amOperator := am.NewOperator(ksClient, k8sClient, fakeInformerFactory, nil)
authorizer := rbac.NewRBACAuthorizer(amOperator)
return New(fakeInformerFactory, k8sClient, ksClient, nil, nil, nil, amOperator, authorizer, nil, nil, nil)
return New(fakeInformerFactory, k8sClient, ksClient, nil, nil, nil, amOperator, nil, authorizer, nil, nil, nil)
}

View File

@@ -58,6 +58,7 @@ const (
GlobalRoleAnnotation = "iam.kubesphere.io/globalrole"
WorkspaceRoleAnnotation = "iam.kubesphere.io/workspacerole"
ClusterRoleAnnotation = "iam.kubesphere.io/clusterrole"
GrantedClustersAnnotation = "iam.kubesphere.io/granted-clusters"
UninitializedAnnotation = "iam.kubesphere.io/uninitialized"
LastPasswordChangeTimeAnnotation = "iam.kubesphere.io/last-password-change-time"
RoleAnnotation = "iam.kubesphere.io/role"