Merge pull request #2164 from wansir/controller
improve multicluster resource controller
This commit is contained in:
@@ -18,7 +18,11 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/application"
|
"kubesphere.io/kubesphere/pkg/controller/application"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/certificatesigningrequest"
|
"kubesphere.io/kubesphere/pkg/controller/certificatesigningrequest"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/cluster"
|
"kubesphere.io/kubesphere/pkg/controller/cluster"
|
||||||
@@ -26,6 +30,7 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/controller/destinationrule"
|
"kubesphere.io/kubesphere/pkg/controller/destinationrule"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/devopscredential"
|
"kubesphere.io/kubesphere/pkg/controller/devopscredential"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/devopsproject"
|
"kubesphere.io/kubesphere/pkg/controller/devopsproject"
|
||||||
|
"kubesphere.io/kubesphere/pkg/controller/globalrole"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/globalrolebinding"
|
"kubesphere.io/kubesphere/pkg/controller/globalrolebinding"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/job"
|
"kubesphere.io/kubesphere/pkg/controller/job"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
|
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
|
||||||
@@ -37,11 +42,15 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/controller/storage/expansion"
|
"kubesphere.io/kubesphere/pkg/controller/storage/expansion"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/user"
|
"kubesphere.io/kubesphere/pkg/controller/user"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/virtualservice"
|
"kubesphere.io/kubesphere/pkg/controller/virtualservice"
|
||||||
|
"kubesphere.io/kubesphere/pkg/controller/workspacerole"
|
||||||
|
"kubesphere.io/kubesphere/pkg/controller/workspacerolebinding"
|
||||||
|
"kubesphere.io/kubesphere/pkg/controller/workspacetemplate"
|
||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||||
|
"sigs.k8s.io/kubefed/pkg/controller/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddControllers(
|
func AddControllers(
|
||||||
@@ -133,16 +142,86 @@ func AddControllers(
|
|||||||
kubernetesInformer.Apps().V1().ReplicaSets(),
|
kubernetesInformer.Apps().V1().ReplicaSets(),
|
||||||
kubernetesInformer.Apps().V1().StatefulSets())
|
kubernetesInformer.Apps().V1().StatefulSets())
|
||||||
|
|
||||||
userController := user.NewController(client.Kubernetes(), client.KubeSphere(), client.Config(),
|
var fedUserCache, fedGlobalRoleBindingCache, fedGlobalRoleCache,
|
||||||
kubesphereInformer.Iam().V1alpha2().Users())
|
fedWorkspaceCache, fedWorkspaceRoleCache, fedWorkspaceRoleBindingCache cache.Store
|
||||||
|
var fedUserCacheController, fedGlobalRoleBindingCacheController, fedGlobalRoleCacheController,
|
||||||
|
fedWorkspaceCacheController, fedWorkspaceRoleCacheController, fedWorkspaceRoleBindingCacheController cache.Controller
|
||||||
|
|
||||||
csrController := certificatesigningrequest.NewController(client.Kubernetes(), kubernetesInformer, client.Config())
|
if multiClusterEnabled {
|
||||||
|
|
||||||
|
fedUserClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedUserResource)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fedGlobalRoleClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedGlobalRoleResource)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fedGlobalRoleBindingClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedGlobalRoleBindingResource)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fedWorkspaceClient, err := util.NewResourceClient(client.Config(), &tenantv1alpha2.FedWorkspaceResource)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fedWorkspaceRoleClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedWorkspaceRoleResource)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fedWorkspaceRoleBindingClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedWorkspaceRoleBindingResource)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fedUserCache, fedUserCacheController = util.NewResourceInformer(fedUserClient, "", &iamv1alpha2.FedUserResource, func(object runtime.Object) {})
|
||||||
|
fedGlobalRoleCache, fedGlobalRoleCacheController = util.NewResourceInformer(fedGlobalRoleClient, "", &iamv1alpha2.FedGlobalRoleResource, func(object runtime.Object) {})
|
||||||
|
fedGlobalRoleBindingCache, fedGlobalRoleBindingCacheController = util.NewResourceInformer(fedGlobalRoleBindingClient, "", &iamv1alpha2.FedGlobalRoleBindingResource, func(object runtime.Object) {})
|
||||||
|
fedWorkspaceCache, fedWorkspaceCacheController = util.NewResourceInformer(fedWorkspaceClient, "", &tenantv1alpha2.FedWorkspaceResource, func(object runtime.Object) {})
|
||||||
|
fedWorkspaceRoleCache, fedWorkspaceRoleCacheController = util.NewResourceInformer(fedWorkspaceRoleClient, "", &iamv1alpha2.FedWorkspaceRoleResource, func(object runtime.Object) {})
|
||||||
|
fedWorkspaceRoleBindingCache, fedWorkspaceRoleBindingCacheController = util.NewResourceInformer(fedWorkspaceRoleBindingClient, "", &iamv1alpha2.FedWorkspaceRoleBindingResource, func(object runtime.Object) {})
|
||||||
|
|
||||||
|
go fedUserCacheController.Run(stopCh)
|
||||||
|
go fedGlobalRoleCacheController.Run(stopCh)
|
||||||
|
go fedGlobalRoleBindingCacheController.Run(stopCh)
|
||||||
|
go fedWorkspaceCacheController.Run(stopCh)
|
||||||
|
go fedWorkspaceRoleCacheController.Run(stopCh)
|
||||||
|
go fedWorkspaceRoleBindingCacheController.Run(stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
userController := user.NewController(client.Kubernetes(), client.KubeSphere(), client.Config(),
|
||||||
|
kubesphereInformer.Iam().V1alpha2().Users(),
|
||||||
|
fedUserCache, fedUserCacheController, kubernetesInformer.Core().V1().ConfigMaps(), multiClusterEnabled)
|
||||||
|
|
||||||
|
csrController := certificatesigningrequest.NewController(client.Kubernetes(), kubernetesInformer.Certificates().V1beta1().CertificateSigningRequests(),
|
||||||
|
kubernetesInformer.Core().V1().ConfigMaps(), client.Config())
|
||||||
|
|
||||||
clusterRoleBindingController := clusterrolebinding.NewController(client.Kubernetes(),
|
clusterRoleBindingController := clusterrolebinding.NewController(client.Kubernetes(),
|
||||||
kubernetesInformer.Rbac().V1().ClusterRoleBindings(), kubernetesInformer.Apps().V1().Deployments(),
|
kubernetesInformer.Rbac().V1().ClusterRoleBindings(), kubernetesInformer.Apps().V1().Deployments(),
|
||||||
kubernetesInformer.Core().V1().Pods(), kubesphereInformer.Iam().V1alpha2().Users())
|
kubernetesInformer.Core().V1().Pods(), kubesphereInformer.Iam().V1alpha2().Users())
|
||||||
|
|
||||||
globalRoleBindingController := globalrolebinding.NewController(client.Kubernetes(), kubesphereInformer.Iam().V1alpha2().GlobalRoleBindings(), multiClusterEnabled)
|
globalRoleController := globalrole.NewController(client.Kubernetes(), client.KubeSphere(),
|
||||||
|
kubesphereInformer.Iam().V1alpha2().GlobalRoles(), fedGlobalRoleCache, fedGlobalRoleCacheController)
|
||||||
|
|
||||||
|
workspaceRoleController := workspacerole.NewController(client.Kubernetes(), client.KubeSphere(),
|
||||||
|
kubesphereInformer.Iam().V1alpha2().WorkspaceRoles(), fedWorkspaceRoleCache, fedWorkspaceRoleCacheController)
|
||||||
|
|
||||||
|
globalRoleBindingController := globalrolebinding.NewController(client.Kubernetes(), client.KubeSphere(),
|
||||||
|
kubesphereInformer.Iam().V1alpha2().GlobalRoleBindings(), fedGlobalRoleBindingCache, fedGlobalRoleBindingCacheController, multiClusterEnabled)
|
||||||
|
|
||||||
|
workspaceRoleBindingController := workspacerolebinding.NewController(client.Kubernetes(), client.KubeSphere(),
|
||||||
|
kubesphereInformer.Iam().V1alpha2().WorkspaceRoleBindings(), fedWorkspaceRoleBindingCache, fedWorkspaceRoleBindingCacheController)
|
||||||
|
|
||||||
|
workspaceTemplateController := workspacetemplate.NewController(client.Kubernetes(), client.KubeSphere(),
|
||||||
|
kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(), kubesphereInformer.Tenant().V1alpha1().Workspaces(),
|
||||||
|
kubesphereInformer.Iam().V1alpha2().RoleBases(), kubesphereInformer.Iam().V1alpha2().WorkspaceRoles(),
|
||||||
|
fedWorkspaceCache, fedWorkspaceCacheController, multiClusterEnabled)
|
||||||
|
|
||||||
clusterController := cluster.NewClusterController(
|
clusterController := cluster.NewClusterController(
|
||||||
client.Kubernetes(),
|
client.Kubernetes(),
|
||||||
@@ -169,21 +248,31 @@ func AddControllers(
|
|||||||
"s2ibinary-controller": s2iBinaryController,
|
"s2ibinary-controller": s2iBinaryController,
|
||||||
"s2irun-controller": s2iRunController,
|
"s2irun-controller": s2iRunController,
|
||||||
"volumeexpansion-controller": volumeExpansionController,
|
"volumeexpansion-controller": volumeExpansionController,
|
||||||
"devopsprojects-controller": devopsProjectController,
|
|
||||||
"pipeline-controller": devopsPipelineController,
|
|
||||||
"devopscredential-controller": devopsCredentialController,
|
|
||||||
"user-controller": userController,
|
"user-controller": userController,
|
||||||
"cluster-controller": clusterController,
|
"cluster-controller": clusterController,
|
||||||
"nsnp-controller": nsnpController,
|
"nsnp-controller": nsnpController,
|
||||||
"csr-controller": csrController,
|
"csr-controller": csrController,
|
||||||
"clusterrolebinding-controller": clusterRoleBindingController,
|
"clusterrolebinding-controller": clusterRoleBindingController,
|
||||||
"globalrolebinding-controller": globalRoleBindingController,
|
"globalrolebinding-controller": globalRoleBindingController,
|
||||||
|
"workspacetemplate-controller": workspaceTemplateController,
|
||||||
|
}
|
||||||
|
|
||||||
|
if devopsClient != nil {
|
||||||
|
controllers["pipeline-controller"] = devopsPipelineController
|
||||||
|
controllers["devopsprojects-controller"] = devopsProjectController
|
||||||
|
controllers["devopscredential-controller"] = devopsCredentialController
|
||||||
}
|
}
|
||||||
|
|
||||||
if storageCapabilityController.IsValidKubernetesVersion() {
|
if storageCapabilityController.IsValidKubernetesVersion() {
|
||||||
controllers["storagecapability-controller"] = storageCapabilityController
|
controllers["storagecapability-controller"] = storageCapabilityController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if multiClusterEnabled {
|
||||||
|
controllers["globalrole-controller"] = globalRoleController
|
||||||
|
controllers["workspacerole-controller"] = workspaceRoleController
|
||||||
|
controllers["workspacerolebinding-controller"] = workspaceRoleBindingController
|
||||||
|
}
|
||||||
|
|
||||||
for name, ctrl := range controllers {
|
for name, ctrl := range controllers {
|
||||||
if err := mgr.Add(ctrl); err != nil {
|
if err := mgr.Add(ctrl); err != nil {
|
||||||
klog.Error(err, "add controller to manager failed", "name", name)
|
klog.Error(err, "add controller to manager failed", "name", name)
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
|
|
||||||
---
|
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
|
||||||
kind: CustomResourceDefinition
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
controller-gen.kubebuilder.io/version: (devel)
|
|
||||||
creationTimestamp: null
|
|
||||||
name: federatedclusterrolebindings.iam.kubesphere.io
|
|
||||||
spec:
|
|
||||||
group: iam.kubesphere.io
|
|
||||||
names:
|
|
||||||
kind: FederatedClusterRoleBinding
|
|
||||||
listKind: FederatedClusterRoleBindingList
|
|
||||||
plural: federatedclusterrolebindings
|
|
||||||
singular: federatedclusterrolebinding
|
|
||||||
scope: Namespaced
|
|
||||||
validation:
|
|
||||||
openAPIV3Schema:
|
|
||||||
properties:
|
|
||||||
apiVersion:
|
|
||||||
description: 'APIVersion defines the versioned schema of this representation
|
|
||||||
of an object. Servers should convert recognized schemas to the latest
|
|
||||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
|
||||||
type: string
|
|
||||||
kind:
|
|
||||||
description: 'Kind is a string value representing the REST resource this
|
|
||||||
object represents. Servers may infer this from the endpoint the client
|
|
||||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
|
||||||
type: string
|
|
||||||
metadata:
|
|
||||||
type: object
|
|
||||||
spec:
|
|
||||||
properties:
|
|
||||||
placement:
|
|
||||||
properties:
|
|
||||||
clusterSelector:
|
|
||||||
properties:
|
|
||||||
matchLabels:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
clusters:
|
|
||||||
items:
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- name
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
type: object
|
|
||||||
template:
|
|
||||||
properties:
|
|
||||||
roleRef:
|
|
||||||
description: RoleRef contains information that points to the role
|
|
||||||
being used
|
|
||||||
properties:
|
|
||||||
apiGroup:
|
|
||||||
description: APIGroup is the group for the resource being referenced
|
|
||||||
type: string
|
|
||||||
kind:
|
|
||||||
description: Kind is the type of resource being referenced
|
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
description: Name is the name of resource being referenced
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- apiGroup
|
|
||||||
- kind
|
|
||||||
- name
|
|
||||||
type: object
|
|
||||||
subjects:
|
|
||||||
items:
|
|
||||||
description: Subject contains a reference to the object or user
|
|
||||||
identities a role binding applies to. This can either hold
|
|
||||||
a direct API object reference, or a value for non-objects such
|
|
||||||
as user and group names.
|
|
||||||
properties:
|
|
||||||
apiGroup:
|
|
||||||
description: APIGroup holds the API group of the referenced
|
|
||||||
subject. Defaults to "" for ServiceAccount subjects. Defaults
|
|
||||||
to "rbac.authorization.k8s.io" for User and Group subjects.
|
|
||||||
type: string
|
|
||||||
kind:
|
|
||||||
description: Kind of object being referenced. Values defined
|
|
||||||
by this API group are "User", "Group", and "ServiceAccount".
|
|
||||||
If the Authorizer does not recognized the kind value, the
|
|
||||||
Authorizer should report an error.
|
|
||||||
type: string
|
|
||||||
name:
|
|
||||||
description: Name of the object being referenced.
|
|
||||||
type: string
|
|
||||||
namespace:
|
|
||||||
description: Namespace of the referenced object. If the object
|
|
||||||
kind is non-namespace, such as "User" or "Group", and this
|
|
||||||
value is not empty the Authorizer should report an error.
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- kind
|
|
||||||
- name
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
required:
|
|
||||||
- roleRef
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- placement
|
|
||||||
- template
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- spec
|
|
||||||
type: object
|
|
||||||
version: v1alpha2
|
|
||||||
versions:
|
|
||||||
- name: v1alpha2
|
|
||||||
served: true
|
|
||||||
storage: true
|
|
||||||
status:
|
|
||||||
acceptedNames:
|
|
||||||
kind: ""
|
|
||||||
plural: ""
|
|
||||||
conditions: []
|
|
||||||
storedVersions: []
|
|
||||||
50
config/crds/iam.kubesphere.io_rolebases.yaml
generated
Normal file
50
config/crds/iam.kubesphere.io_rolebases.yaml
generated
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: (devel)
|
||||||
|
creationTimestamp: null
|
||||||
|
name: rolebases.iam.kubesphere.io
|
||||||
|
spec:
|
||||||
|
group: iam.kubesphere.io
|
||||||
|
names:
|
||||||
|
categories:
|
||||||
|
- iam
|
||||||
|
kind: RoleBase
|
||||||
|
listKind: RoleBaseList
|
||||||
|
plural: rolebases
|
||||||
|
singular: rolebase
|
||||||
|
scope: Cluster
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
role:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- role
|
||||||
|
type: object
|
||||||
|
version: v1alpha2
|
||||||
|
versions:
|
||||||
|
- name: v1alpha2
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
@@ -44,6 +44,35 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
networkIsolation:
|
networkIsolation:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
overrides:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
clusterName:
|
||||||
|
type: string
|
||||||
|
clusterOverrides:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
op:
|
||||||
|
type: string
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
anyOf:
|
||||||
|
- type: string
|
||||||
|
- type: integer
|
||||||
|
- type: boolean
|
||||||
|
- type: object
|
||||||
|
- type: array
|
||||||
|
required:
|
||||||
|
- path
|
||||||
|
- value
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- clusterName
|
||||||
|
- clusterOverrides
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
version: v1alpha2
|
version: v1alpha2
|
||||||
|
|||||||
161
pkg/apis/iam/v1alpha2/federated_types.go
Normal file
161
pkg/apis/iam/v1alpha2/federated_types.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResourcesSingularFedUser = "federateduser"
|
||||||
|
ResourcesSingularFedGlobalRoleBinding = "federatedglobalrolebinding"
|
||||||
|
ResourcesSingularFedWorkspaceRoleBinding = "federatedworkspacerolebinding"
|
||||||
|
ResourcesSingularFedGlobalRole = "federatedglobalrole"
|
||||||
|
ResourcesSingularFedWorkspaceRole = "federatedworkspacerole"
|
||||||
|
ResourcesPluralFedUser = "federatedusers"
|
||||||
|
ResourcesPluralFedGlobalRoleBinding = "federatedglobalrolebindings"
|
||||||
|
ResourcesPluralFedWorkspaceRoleBinding = "federatedworkspacerolebindings"
|
||||||
|
ResourcesPluralFedGlobalRole = "federatedglobalroles"
|
||||||
|
ResourcesPluralFedWorkspaceRole = "federatedworkspaceroles"
|
||||||
|
FedClusterRoleBindingKind = "FederatedClusterRoleBinding"
|
||||||
|
FedClusterRoleKind = "FederatedClusterRole"
|
||||||
|
FedGlobalRoleKind = "FederatedGlobalRole"
|
||||||
|
FedWorkspaceRoleKind = "FederatedWorkspaceRole"
|
||||||
|
FedGlobalRoleBindingKind = "FederatedGlobalRoleBinding"
|
||||||
|
FedWorkspaceRoleBindingKind = "FederatedWorkspaceRoleBinding"
|
||||||
|
fedResourceGroup = "types.kubefed.io"
|
||||||
|
fedResourceVersion = "v1beta1"
|
||||||
|
FedUserKind = "FederatedUser"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FedUserResource = metav1.APIResource{
|
||||||
|
Name: ResourcesPluralFedUser,
|
||||||
|
SingularName: ResourcesSingularFedUser,
|
||||||
|
Namespaced: false,
|
||||||
|
Group: fedResourceGroup,
|
||||||
|
Version: fedResourceVersion,
|
||||||
|
Kind: FedUserKind,
|
||||||
|
}
|
||||||
|
FedGlobalRoleBindingResource = metav1.APIResource{
|
||||||
|
Name: ResourcesPluralFedGlobalRoleBinding,
|
||||||
|
SingularName: ResourcesSingularFedGlobalRoleBinding,
|
||||||
|
Namespaced: false,
|
||||||
|
Group: fedResourceGroup,
|
||||||
|
Version: fedResourceVersion,
|
||||||
|
Kind: FedGlobalRoleBindingKind,
|
||||||
|
}
|
||||||
|
FedWorkspaceRoleBindingResource = metav1.APIResource{
|
||||||
|
Name: ResourcesPluralFedWorkspaceRoleBinding,
|
||||||
|
SingularName: ResourcesSingularFedWorkspaceRoleBinding,
|
||||||
|
Namespaced: false,
|
||||||
|
Group: fedResourceGroup,
|
||||||
|
Version: fedResourceVersion,
|
||||||
|
Kind: FedWorkspaceRoleBindingKind,
|
||||||
|
}
|
||||||
|
FedGlobalRoleResource = metav1.APIResource{
|
||||||
|
Name: ResourcesPluralFedGlobalRole,
|
||||||
|
SingularName: ResourcesSingularFedGlobalRole,
|
||||||
|
Namespaced: false,
|
||||||
|
Group: fedResourceGroup,
|
||||||
|
Version: fedResourceVersion,
|
||||||
|
Kind: FedGlobalRoleKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
FedWorkspaceRoleResource = metav1.APIResource{
|
||||||
|
Name: ResourcesPluralFedWorkspaceRole,
|
||||||
|
SingularName: ResourcesSingularFedWorkspaceRole,
|
||||||
|
Namespaced: false,
|
||||||
|
Group: fedResourceGroup,
|
||||||
|
Version: fedResourceVersion,
|
||||||
|
Kind: FedWorkspaceRoleKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
FederatedClusterRoleBindingResource = schema.GroupVersionResource{
|
||||||
|
Group: fedResourceGroup,
|
||||||
|
Version: fedResourceVersion,
|
||||||
|
Resource: "federatedclusterrolebindings",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type FederatedRoleBinding struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Spec FederatedRoleBindingSpec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FederatedRoleBindingSpec struct {
|
||||||
|
Template RoleBindingTemplate `json:"template"`
|
||||||
|
Placement Placement `json:"placement"`
|
||||||
|
}
|
||||||
|
type RoleBindingTemplate struct {
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Subjects []rbacv1.Subject `json:"subjects,omitempty"`
|
||||||
|
RoleRef rbacv1.RoleRef `json:"roleRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FederatedRole struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Spec FederatedRoleSpec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FederatedRoleSpec struct {
|
||||||
|
Template RoleTemplate `json:"template"`
|
||||||
|
Placement Placement `json:"placement"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleTemplate struct {
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
// +optional
|
||||||
|
Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FederatedUser struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Spec FederatedUserSpec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FederatedUserSpec struct {
|
||||||
|
Template UserTemplate `json:"template"`
|
||||||
|
Placement Placement `json:"placement"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserTemplate struct {
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Spec UserSpec `json:"spec"`
|
||||||
|
// +optional
|
||||||
|
Status UserStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Placement struct {
|
||||||
|
Clusters []Cluster `json:"clusters,omitempty"`
|
||||||
|
ClusterSelector ClusterSelector `json:"clusterSelector,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClusterSelector struct {
|
||||||
|
MatchLabels map[string]string `json:"matchLabels,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cluster struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
@@ -59,7 +59,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
|||||||
&WorkspaceRoleList{},
|
&WorkspaceRoleList{},
|
||||||
&WorkspaceRoleBinding{},
|
&WorkspaceRoleBinding{},
|
||||||
&WorkspaceRoleBindingList{},
|
&WorkspaceRoleBindingList{},
|
||||||
&FederatedClusterRoleBinding{},
|
&RoleBase{},
|
||||||
|
&RoleBaseList{},
|
||||||
)
|
)
|
||||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package v1alpha2
|
|||||||
import (
|
import (
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -65,6 +66,7 @@ const (
|
|||||||
ScopeCluster = "cluster"
|
ScopeCluster = "cluster"
|
||||||
ScopeNamespace = "namespace"
|
ScopeNamespace = "namespace"
|
||||||
PlatformAdmin = "platform-admin"
|
PlatformAdmin = "platform-admin"
|
||||||
|
NamespaceAdmin = "admin"
|
||||||
ClusterAdmin = "cluster-admin"
|
ClusterAdmin = "cluster-admin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -284,31 +286,22 @@ type WorkspaceRoleBindingList struct {
|
|||||||
Items []WorkspaceRoleBinding `json:"items"`
|
Items []WorkspaceRoleBinding `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +genclient:nonNamespaced
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
type FederatedClusterRoleBinding struct {
|
// +kubebuilder:resource:categories="iam",scope="Cluster"
|
||||||
|
type RoleBase struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
Spec FederatedClusterRoleBindingSpec `json:"spec"`
|
|
||||||
|
Role runtime.RawExtension `json:"role"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FederatedClusterRoleBindingSpec struct {
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
Template Template `json:"template"`
|
|
||||||
Placement Placement `json:"placement"`
|
|
||||||
}
|
|
||||||
type Template struct {
|
|
||||||
Subjects []rbacv1.Subject `json:"subjects,omitempty"`
|
|
||||||
RoleRef rbacv1.RoleRef `json:"roleRef"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Placement struct {
|
// RoleBaseList contains a list of RoleBase
|
||||||
Clusters []Cluster `json:"clusters,omitempty"`
|
type RoleBaseList struct {
|
||||||
ClusterSelector ClusterSelector `json:"clusterSelector,omitempty"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
}
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []RoleBase `json:"items"`
|
||||||
type ClusterSelector struct {
|
|
||||||
MatchLabels map[string]string `json:"matchLabels,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cluster struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
194
pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go
generated
194
pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go
generated
@@ -63,44 +63,106 @@ func (in *ClusterSelector) DeepCopy() *ClusterSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *FederatedClusterRoleBinding) DeepCopyInto(out *FederatedClusterRoleBinding) {
|
func (in *FederatedRole) DeepCopyInto(out *FederatedRole) {
|
||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
in.Spec.DeepCopyInto(&out.Spec)
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedClusterRoleBinding.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedRole.
|
||||||
func (in *FederatedClusterRoleBinding) DeepCopy() *FederatedClusterRoleBinding {
|
func (in *FederatedRole) DeepCopy() *FederatedRole {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(FederatedClusterRoleBinding)
|
out := new(FederatedRole)
|
||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *FederatedClusterRoleBinding) DeepCopyObject() runtime.Object {
|
func (in *FederatedRoleBinding) DeepCopyInto(out *FederatedRoleBinding) {
|
||||||
if c := in.DeepCopy(); c != nil {
|
*out = *in
|
||||||
return c
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedRoleBinding.
|
||||||
|
func (in *FederatedRoleBinding) DeepCopy() *FederatedRoleBinding {
|
||||||
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
out := new(FederatedRoleBinding)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *FederatedClusterRoleBindingSpec) DeepCopyInto(out *FederatedClusterRoleBindingSpec) {
|
func (in *FederatedRoleBindingSpec) DeepCopyInto(out *FederatedRoleBindingSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
in.Template.DeepCopyInto(&out.Template)
|
in.Template.DeepCopyInto(&out.Template)
|
||||||
in.Placement.DeepCopyInto(&out.Placement)
|
in.Placement.DeepCopyInto(&out.Placement)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedClusterRoleBindingSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedRoleBindingSpec.
|
||||||
func (in *FederatedClusterRoleBindingSpec) DeepCopy() *FederatedClusterRoleBindingSpec {
|
func (in *FederatedRoleBindingSpec) DeepCopy() *FederatedRoleBindingSpec {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(FederatedClusterRoleBindingSpec)
|
out := new(FederatedRoleBindingSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *FederatedRoleSpec) DeepCopyInto(out *FederatedRoleSpec) {
|
||||||
|
*out = *in
|
||||||
|
in.Template.DeepCopyInto(&out.Template)
|
||||||
|
in.Placement.DeepCopyInto(&out.Placement)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedRoleSpec.
|
||||||
|
func (in *FederatedRoleSpec) DeepCopy() *FederatedRoleSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(FederatedRoleSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *FederatedUser) DeepCopyInto(out *FederatedUser) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedUser.
|
||||||
|
func (in *FederatedUser) DeepCopy() *FederatedUser {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(FederatedUser)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *FederatedUserSpec) DeepCopyInto(out *FederatedUserSpec) {
|
||||||
|
*out = *in
|
||||||
|
in.Template.DeepCopyInto(&out.Template)
|
||||||
|
in.Placement.DeepCopyInto(&out.Placement)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedUserSpec.
|
||||||
|
func (in *FederatedUserSpec) DeepCopy() *FederatedUserSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(FederatedUserSpec)
|
||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
@@ -254,8 +316,67 @@ func (in *Placement) DeepCopy() *Placement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Template) DeepCopyInto(out *Template) {
|
func (in *RoleBase) DeepCopyInto(out *RoleBase) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Role.DeepCopyInto(&out.Role)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBase.
|
||||||
|
func (in *RoleBase) DeepCopy() *RoleBase {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RoleBase)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *RoleBase) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RoleBaseList) DeepCopyInto(out *RoleBaseList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]RoleBase, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBaseList.
|
||||||
|
func (in *RoleBaseList) DeepCopy() *RoleBaseList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RoleBaseList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *RoleBaseList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RoleBindingTemplate) DeepCopyInto(out *RoleBindingTemplate) {
|
||||||
|
*out = *in
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
if in.Subjects != nil {
|
if in.Subjects != nil {
|
||||||
in, out := &in.Subjects, &out.Subjects
|
in, out := &in.Subjects, &out.Subjects
|
||||||
*out = make([]v1.Subject, len(*in))
|
*out = make([]v1.Subject, len(*in))
|
||||||
@@ -264,12 +385,35 @@ func (in *Template) DeepCopyInto(out *Template) {
|
|||||||
out.RoleRef = in.RoleRef
|
out.RoleRef = in.RoleRef
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Template.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBindingTemplate.
|
||||||
func (in *Template) DeepCopy() *Template {
|
func (in *RoleBindingTemplate) DeepCopy() *RoleBindingTemplate {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(Template)
|
out := new(RoleBindingTemplate)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RoleTemplate) DeepCopyInto(out *RoleTemplate) {
|
||||||
|
*out = *in
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
if in.Rules != nil {
|
||||||
|
in, out := &in.Rules, &out.Rules
|
||||||
|
*out = make([]v1.PolicyRule, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleTemplate.
|
||||||
|
func (in *RoleTemplate) DeepCopy() *RoleTemplate {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RoleTemplate)
|
||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
@@ -391,6 +535,24 @@ func (in *UserStatus) DeepCopy() *UserStatus {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *UserTemplate) DeepCopyInto(out *UserTemplate) {
|
||||||
|
*out = *in
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserTemplate.
|
||||||
|
func (in *UserTemplate) DeepCopy() *UserTemplate {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(UserTemplate)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *WorkspaceRole) DeepCopyInto(out *WorkspaceRole) {
|
func (in *WorkspaceRole) DeepCopyInto(out *WorkspaceRole) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package v1alpha2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,6 +26,22 @@ const (
|
|||||||
ResourceKindWorkspaceTemplate = "WorkspaceTemplate"
|
ResourceKindWorkspaceTemplate = "WorkspaceTemplate"
|
||||||
ResourceSingularWorkspaceTemplate = "workspacetemplate"
|
ResourceSingularWorkspaceTemplate = "workspacetemplate"
|
||||||
ResourcePluralWorkspaceTemplate = "workspacetemplates"
|
ResourcePluralWorkspaceTemplate = "workspacetemplates"
|
||||||
|
ResourcesPluralFedWorkspace = "federatedworkspaces"
|
||||||
|
ResourcesSingularFedWorkspace = "federatedworkspace"
|
||||||
|
FedWorkspaceKind = "FederatedWorkspace"
|
||||||
|
fedResourceGroup = "types.kubefed.io"
|
||||||
|
fedResourceVersion = "v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FedWorkspaceResource = metav1.APIResource{
|
||||||
|
Name: ResourcesPluralFedWorkspace,
|
||||||
|
SingularName: ResourcesSingularFedWorkspace,
|
||||||
|
Namespaced: false,
|
||||||
|
Group: fedResourceGroup,
|
||||||
|
Version: fedResourceVersion,
|
||||||
|
Kind: FedWorkspaceKind,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// +genclient
|
// +genclient
|
||||||
@@ -45,6 +62,7 @@ type WorkspaceTemplateSpec struct {
|
|||||||
// authorized clusters
|
// authorized clusters
|
||||||
// +optional
|
// +optional
|
||||||
Clusters []string `json:"clusters,omitempty"`
|
Clusters []string `json:"clusters,omitempty"`
|
||||||
|
Overrides []Override `json:"overrides,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
@@ -60,3 +78,44 @@ type WorkspaceTemplateList struct {
|
|||||||
func init() {
|
func init() {
|
||||||
SchemeBuilder.Register(&WorkspaceTemplate{}, &WorkspaceTemplateList{})
|
SchemeBuilder.Register(&WorkspaceTemplate{}, &WorkspaceTemplateList{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FederatedWorkspace struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Spec FederatedWorkspaceSpec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FederatedWorkspaceSpec struct {
|
||||||
|
Template Template `json:"template"`
|
||||||
|
Placement Placement `json:"placement"`
|
||||||
|
Overrides []Override `json:"overrides,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Template struct {
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Spec v1alpha1.WorkspaceSpec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Placement struct {
|
||||||
|
Clusters []Cluster `json:"clusters,omitempty"`
|
||||||
|
ClusterSelector ClusterSelector `json:"clusterSelector,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClusterSelector struct {
|
||||||
|
MatchLabels map[string]string `json:"matchLabels,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cluster struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Override struct {
|
||||||
|
ClusterName string `json:"clusterName"`
|
||||||
|
ClusterOverrides []ClusterOverride `json:"clusterOverrides"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClusterOverride struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Op string `json:"op,omitempty"`
|
||||||
|
Value runtime.RawExtension `json:"value"`
|
||||||
|
}
|
||||||
|
|||||||
164
pkg/apis/tenant/v1alpha2/zz_generated.deepcopy.go
generated
164
pkg/apis/tenant/v1alpha2/zz_generated.deepcopy.go
generated
@@ -21,9 +21,164 @@ limitations under the License.
|
|||||||
package v1alpha2
|
package v1alpha2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster.
|
||||||
|
func (in *Cluster) DeepCopy() *Cluster {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Cluster)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ClusterOverride) DeepCopyInto(out *ClusterOverride) {
|
||||||
|
*out = *in
|
||||||
|
in.Value.DeepCopyInto(&out.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterOverride.
|
||||||
|
func (in *ClusterOverride) DeepCopy() *ClusterOverride {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ClusterOverride)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ClusterSelector) DeepCopyInto(out *ClusterSelector) {
|
||||||
|
*out = *in
|
||||||
|
if in.MatchLabels != nil {
|
||||||
|
in, out := &in.MatchLabels, &out.MatchLabels
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSelector.
|
||||||
|
func (in *ClusterSelector) DeepCopy() *ClusterSelector {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ClusterSelector)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *FederatedWorkspace) DeepCopyInto(out *FederatedWorkspace) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspace.
|
||||||
|
func (in *FederatedWorkspace) DeepCopy() *FederatedWorkspace {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(FederatedWorkspace)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *FederatedWorkspaceSpec) DeepCopyInto(out *FederatedWorkspaceSpec) {
|
||||||
|
*out = *in
|
||||||
|
in.Template.DeepCopyInto(&out.Template)
|
||||||
|
in.Placement.DeepCopyInto(&out.Placement)
|
||||||
|
if in.Overrides != nil {
|
||||||
|
in, out := &in.Overrides, &out.Overrides
|
||||||
|
*out = make([]Override, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceSpec.
|
||||||
|
func (in *FederatedWorkspaceSpec) DeepCopy() *FederatedWorkspaceSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(FederatedWorkspaceSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Override) DeepCopyInto(out *Override) {
|
||||||
|
*out = *in
|
||||||
|
if in.ClusterOverrides != nil {
|
||||||
|
in, out := &in.ClusterOverrides, &out.ClusterOverrides
|
||||||
|
*out = make([]ClusterOverride, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Override.
|
||||||
|
func (in *Override) DeepCopy() *Override {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Override)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Placement) DeepCopyInto(out *Placement) {
|
||||||
|
*out = *in
|
||||||
|
if in.Clusters != nil {
|
||||||
|
in, out := &in.Clusters, &out.Clusters
|
||||||
|
*out = make([]Cluster, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
in.ClusterSelector.DeepCopyInto(&out.ClusterSelector)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Placement.
|
||||||
|
func (in *Placement) DeepCopy() *Placement {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Placement)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Template) DeepCopyInto(out *Template) {
|
||||||
|
*out = *in
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
out.Spec = in.Spec
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Template.
|
||||||
|
func (in *Template) DeepCopy() *Template {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Template)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *WorkspaceTemplate) DeepCopyInto(out *WorkspaceTemplate) {
|
func (in *WorkspaceTemplate) DeepCopyInto(out *WorkspaceTemplate) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -91,6 +246,13 @@ func (in *WorkspaceTemplateSpec) DeepCopyInto(out *WorkspaceTemplateSpec) {
|
|||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.Overrides != nil {
|
||||||
|
in, out := &in.Overrides, &out.Overrides
|
||||||
|
*out = make([]Override, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceTemplateSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceTemplateSpec.
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ func (c *FakeIamV1alpha2) GlobalRoleBindings() v1alpha2.GlobalRoleBindingInterfa
|
|||||||
return &FakeGlobalRoleBindings{c}
|
return &FakeGlobalRoleBindings{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FakeIamV1alpha2) RoleBases() v1alpha2.RoleBaseInterface {
|
||||||
|
return &FakeRoleBases{c}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *FakeIamV1alpha2) Users() v1alpha2.UserInterface {
|
func (c *FakeIamV1alpha2) Users() v1alpha2.UserInterface {
|
||||||
return &FakeUsers{c}
|
return &FakeUsers{c}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
labels "k8s.io/apimachinery/pkg/labels"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
testing "k8s.io/client-go/testing"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeRoleBases implements RoleBaseInterface
|
||||||
|
type FakeRoleBases struct {
|
||||||
|
Fake *FakeIamV1alpha2
|
||||||
|
}
|
||||||
|
|
||||||
|
var rolebasesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "rolebases"}
|
||||||
|
|
||||||
|
var rolebasesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "RoleBase"}
|
||||||
|
|
||||||
|
// Get takes name of the roleBase, and returns the corresponding roleBase object, and an error if there is any.
|
||||||
|
func (c *FakeRoleBases) Get(name string, options v1.GetOptions) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootGetAction(rolebasesResource, name), &v1alpha2.RoleBase{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.RoleBase), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of RoleBases that match those selectors.
|
||||||
|
func (c *FakeRoleBases) List(opts v1.ListOptions) (result *v1alpha2.RoleBaseList, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootListAction(rolebasesResource, rolebasesKind, opts), &v1alpha2.RoleBaseList{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||||
|
if label == nil {
|
||||||
|
label = labels.Everything()
|
||||||
|
}
|
||||||
|
list := &v1alpha2.RoleBaseList{ListMeta: obj.(*v1alpha2.RoleBaseList).ListMeta}
|
||||||
|
for _, item := range obj.(*v1alpha2.RoleBaseList).Items {
|
||||||
|
if label.Matches(labels.Set(item.Labels)) {
|
||||||
|
list.Items = append(list.Items, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns a watch.Interface that watches the requested roleBases.
|
||||||
|
func (c *FakeRoleBases) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||||
|
return c.Fake.
|
||||||
|
InvokesWatch(testing.NewRootWatchAction(rolebasesResource, opts))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a roleBase and creates it. Returns the server's representation of the roleBase, and an error, if there is any.
|
||||||
|
func (c *FakeRoleBases) Create(roleBase *v1alpha2.RoleBase) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootCreateAction(rolebasesResource, roleBase), &v1alpha2.RoleBase{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.RoleBase), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a roleBase and updates it. Returns the server's representation of the roleBase, and an error, if there is any.
|
||||||
|
func (c *FakeRoleBases) Update(roleBase *v1alpha2.RoleBase) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootUpdateAction(rolebasesResource, roleBase), &v1alpha2.RoleBase{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.RoleBase), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the roleBase and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *FakeRoleBases) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
_, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootDeleteAction(rolebasesResource, name), &v1alpha2.RoleBase{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *FakeRoleBases) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||||
|
action := testing.NewRootDeleteCollectionAction(rolebasesResource, listOptions)
|
||||||
|
|
||||||
|
_, err := c.Fake.Invokes(action, &v1alpha2.RoleBaseList{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched roleBase.
|
||||||
|
func (c *FakeRoleBases) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootPatchSubresourceAction(rolebasesResource, name, pt, data, subresources...), &v1alpha2.RoleBase{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.RoleBase), err
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@ type GlobalRoleExpansion interface{}
|
|||||||
|
|
||||||
type GlobalRoleBindingExpansion interface{}
|
type GlobalRoleBindingExpansion interface{}
|
||||||
|
|
||||||
|
type RoleBaseExpansion interface{}
|
||||||
|
|
||||||
type UserExpansion interface{}
|
type UserExpansion interface{}
|
||||||
|
|
||||||
type WorkspaceRoleExpansion interface{}
|
type WorkspaceRoleExpansion interface{}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type IamV1alpha2Interface interface {
|
|||||||
RESTClient() rest.Interface
|
RESTClient() rest.Interface
|
||||||
GlobalRolesGetter
|
GlobalRolesGetter
|
||||||
GlobalRoleBindingsGetter
|
GlobalRoleBindingsGetter
|
||||||
|
RoleBasesGetter
|
||||||
UsersGetter
|
UsersGetter
|
||||||
WorkspaceRolesGetter
|
WorkspaceRolesGetter
|
||||||
WorkspaceRoleBindingsGetter
|
WorkspaceRoleBindingsGetter
|
||||||
@@ -46,6 +47,10 @@ func (c *IamV1alpha2Client) GlobalRoleBindings() GlobalRoleBindingInterface {
|
|||||||
return newGlobalRoleBindings(c)
|
return newGlobalRoleBindings(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *IamV1alpha2Client) RoleBases() RoleBaseInterface {
|
||||||
|
return newRoleBases(c)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *IamV1alpha2Client) Users() UserInterface {
|
func (c *IamV1alpha2Client) Users() UserInterface {
|
||||||
return newUsers(c)
|
return newUsers(c)
|
||||||
}
|
}
|
||||||
|
|||||||
164
pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebase.go
Normal file
164
pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebase.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoleBasesGetter has a method to return a RoleBaseInterface.
|
||||||
|
// A group's client should implement this interface.
|
||||||
|
type RoleBasesGetter interface {
|
||||||
|
RoleBases() RoleBaseInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleBaseInterface has methods to work with RoleBase resources.
|
||||||
|
type RoleBaseInterface interface {
|
||||||
|
Create(*v1alpha2.RoleBase) (*v1alpha2.RoleBase, error)
|
||||||
|
Update(*v1alpha2.RoleBase) (*v1alpha2.RoleBase, error)
|
||||||
|
Delete(name string, options *v1.DeleteOptions) error
|
||||||
|
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
|
||||||
|
Get(name string, options v1.GetOptions) (*v1alpha2.RoleBase, error)
|
||||||
|
List(opts v1.ListOptions) (*v1alpha2.RoleBaseList, error)
|
||||||
|
Watch(opts v1.ListOptions) (watch.Interface, error)
|
||||||
|
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBase, err error)
|
||||||
|
RoleBaseExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// roleBases implements RoleBaseInterface
|
||||||
|
type roleBases struct {
|
||||||
|
client rest.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRoleBases returns a RoleBases
|
||||||
|
func newRoleBases(c *IamV1alpha2Client) *roleBases {
|
||||||
|
return &roleBases{
|
||||||
|
client: c.RESTClient(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get takes name of the roleBase, and returns the corresponding roleBase object, and an error if there is any.
|
||||||
|
func (c *roleBases) Get(name string, options v1.GetOptions) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
result = &v1alpha2.RoleBase{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Resource("rolebases").
|
||||||
|
Name(name).
|
||||||
|
VersionedParams(&options, scheme.ParameterCodec).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of RoleBases that match those selectors.
|
||||||
|
func (c *roleBases) List(opts v1.ListOptions) (result *v1alpha2.RoleBaseList, err error) {
|
||||||
|
var timeout time.Duration
|
||||||
|
if opts.TimeoutSeconds != nil {
|
||||||
|
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||||
|
}
|
||||||
|
result = &v1alpha2.RoleBaseList{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Resource("rolebases").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Timeout(timeout).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns a watch.Interface that watches the requested roleBases.
|
||||||
|
func (c *roleBases) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||||
|
var timeout time.Duration
|
||||||
|
if opts.TimeoutSeconds != nil {
|
||||||
|
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||||
|
}
|
||||||
|
opts.Watch = true
|
||||||
|
return c.client.Get().
|
||||||
|
Resource("rolebases").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Timeout(timeout).
|
||||||
|
Watch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a roleBase and creates it. Returns the server's representation of the roleBase, and an error, if there is any.
|
||||||
|
func (c *roleBases) Create(roleBase *v1alpha2.RoleBase) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
result = &v1alpha2.RoleBase{}
|
||||||
|
err = c.client.Post().
|
||||||
|
Resource("rolebases").
|
||||||
|
Body(roleBase).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a roleBase and updates it. Returns the server's representation of the roleBase, and an error, if there is any.
|
||||||
|
func (c *roleBases) Update(roleBase *v1alpha2.RoleBase) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
result = &v1alpha2.RoleBase{}
|
||||||
|
err = c.client.Put().
|
||||||
|
Resource("rolebases").
|
||||||
|
Name(roleBase.Name).
|
||||||
|
Body(roleBase).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the roleBase and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *roleBases) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
return c.client.Delete().
|
||||||
|
Resource("rolebases").
|
||||||
|
Name(name).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *roleBases) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||||
|
var timeout time.Duration
|
||||||
|
if listOptions.TimeoutSeconds != nil {
|
||||||
|
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
|
||||||
|
}
|
||||||
|
return c.client.Delete().
|
||||||
|
Resource("rolebases").
|
||||||
|
VersionedParams(&listOptions, scheme.ParameterCodec).
|
||||||
|
Timeout(timeout).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched roleBase.
|
||||||
|
func (c *roleBases) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBase, err error) {
|
||||||
|
result = &v1alpha2.RoleBase{}
|
||||||
|
err = c.client.Patch(pt).
|
||||||
|
Resource("rolebases").
|
||||||
|
SubResource(subresources...).
|
||||||
|
Name(name).
|
||||||
|
Body(data).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
|||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoles().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoles().Informer()}, nil
|
||||||
case v1alpha2.SchemeGroupVersion.WithResource("globalrolebindings"):
|
case v1alpha2.SchemeGroupVersion.WithResource("globalrolebindings"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoleBindings().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoleBindings().Informer()}, nil
|
||||||
|
case v1alpha2.SchemeGroupVersion.WithResource("rolebases"):
|
||||||
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().RoleBases().Informer()}, nil
|
||||||
case v1alpha2.SchemeGroupVersion.WithResource("users"):
|
case v1alpha2.SchemeGroupVersion.WithResource("users"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Users().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Users().Informer()}, nil
|
||||||
case v1alpha2.SchemeGroupVersion.WithResource("workspaceroles"):
|
case v1alpha2.SchemeGroupVersion.WithResource("workspaceroles"):
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ type Interface interface {
|
|||||||
GlobalRoles() GlobalRoleInformer
|
GlobalRoles() GlobalRoleInformer
|
||||||
// GlobalRoleBindings returns a GlobalRoleBindingInformer.
|
// GlobalRoleBindings returns a GlobalRoleBindingInformer.
|
||||||
GlobalRoleBindings() GlobalRoleBindingInformer
|
GlobalRoleBindings() GlobalRoleBindingInformer
|
||||||
|
// RoleBases returns a RoleBaseInformer.
|
||||||
|
RoleBases() RoleBaseInformer
|
||||||
// Users returns a UserInformer.
|
// Users returns a UserInformer.
|
||||||
Users() UserInformer
|
Users() UserInformer
|
||||||
// WorkspaceRoles returns a WorkspaceRoleInformer.
|
// WorkspaceRoles returns a WorkspaceRoleInformer.
|
||||||
@@ -57,6 +59,11 @@ func (v *version) GlobalRoleBindings() GlobalRoleBindingInformer {
|
|||||||
return &globalRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
|
return &globalRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RoleBases returns a RoleBaseInformer.
|
||||||
|
func (v *version) RoleBases() RoleBaseInformer {
|
||||||
|
return &roleBaseInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
|
||||||
|
}
|
||||||
|
|
||||||
// Users returns a UserInformer.
|
// Users returns a UserInformer.
|
||||||
func (v *version) Users() UserInformer {
|
func (v *version) Users() UserInformer {
|
||||||
return &userInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
|
return &userInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
cache "k8s.io/client-go/tools/cache"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
|
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoleBaseInformer provides access to a shared informer and lister for
|
||||||
|
// RoleBases.
|
||||||
|
type RoleBaseInformer interface {
|
||||||
|
Informer() cache.SharedIndexInformer
|
||||||
|
Lister() v1alpha2.RoleBaseLister
|
||||||
|
}
|
||||||
|
|
||||||
|
type roleBaseInformer struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRoleBaseInformer constructs a new informer for RoleBase type.
|
||||||
|
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||||
|
// one. This reduces memory footprint and number of connections to the server.
|
||||||
|
func NewRoleBaseInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredRoleBaseInformer(client, resyncPeriod, indexers, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilteredRoleBaseInformer constructs a new informer for RoleBase type.
|
||||||
|
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||||
|
// one. This reduces memory footprint and number of connections to the server.
|
||||||
|
func NewFilteredRoleBaseInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
|
||||||
|
return cache.NewSharedIndexInformer(
|
||||||
|
&cache.ListWatch{
|
||||||
|
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.IamV1alpha2().RoleBases().List(options)
|
||||||
|
},
|
||||||
|
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.IamV1alpha2().RoleBases().Watch(options)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&iamv1alpha2.RoleBase{},
|
||||||
|
resyncPeriod,
|
||||||
|
indexers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *roleBaseInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredRoleBaseInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *roleBaseInformer) Informer() cache.SharedIndexInformer {
|
||||||
|
return f.factory.InformerFor(&iamv1alpha2.RoleBase{}, f.defaultInformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *roleBaseInformer) Lister() v1alpha2.RoleBaseLister {
|
||||||
|
return v1alpha2.NewRoleBaseLister(f.Informer().GetIndexer())
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ type GlobalRoleListerExpansion interface{}
|
|||||||
// GlobalRoleBindingLister.
|
// GlobalRoleBindingLister.
|
||||||
type GlobalRoleBindingListerExpansion interface{}
|
type GlobalRoleBindingListerExpansion interface{}
|
||||||
|
|
||||||
|
// RoleBaseListerExpansion allows custom methods to be added to
|
||||||
|
// RoleBaseLister.
|
||||||
|
type RoleBaseListerExpansion interface{}
|
||||||
|
|
||||||
// UserListerExpansion allows custom methods to be added to
|
// UserListerExpansion allows custom methods to be added to
|
||||||
// UserLister.
|
// UserLister.
|
||||||
type UserListerExpansion interface{}
|
type UserListerExpansion interface{}
|
||||||
|
|||||||
65
pkg/client/listers/iam/v1alpha2/rolebase.go
Normal file
65
pkg/client/listers/iam/v1alpha2/rolebase.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by lister-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoleBaseLister helps list RoleBases.
|
||||||
|
type RoleBaseLister interface {
|
||||||
|
// List lists all RoleBases in the indexer.
|
||||||
|
List(selector labels.Selector) (ret []*v1alpha2.RoleBase, err error)
|
||||||
|
// Get retrieves the RoleBase from the index for a given name.
|
||||||
|
Get(name string) (*v1alpha2.RoleBase, error)
|
||||||
|
RoleBaseListerExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// roleBaseLister implements the RoleBaseLister interface.
|
||||||
|
type roleBaseLister struct {
|
||||||
|
indexer cache.Indexer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRoleBaseLister returns a new RoleBaseLister.
|
||||||
|
func NewRoleBaseLister(indexer cache.Indexer) RoleBaseLister {
|
||||||
|
return &roleBaseLister{indexer: indexer}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all RoleBases in the indexer.
|
||||||
|
func (s *roleBaseLister) List(selector labels.Selector) (ret []*v1alpha2.RoleBase, err error) {
|
||||||
|
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
|
||||||
|
ret = append(ret, m.(*v1alpha2.RoleBase))
|
||||||
|
})
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the RoleBase from the index for a given name.
|
||||||
|
func (s *roleBaseLister) Get(name string) (*v1alpha2.RoleBase, error) {
|
||||||
|
obj, exists, err := s.indexer.GetByKey(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.NewNotFound(v1alpha2.Resource("rolebase"), name)
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.RoleBase), nil
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The KubeSphere authors.
|
Copyright 2020 The KubeSphere Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ const (
|
|||||||
DevopsOwner = "owner"
|
DevopsOwner = "owner"
|
||||||
DevopsReporter = "reporter"
|
DevopsReporter = "reporter"
|
||||||
DevOpsProjectLabelKey = "kubesphere.io/devopsproject"
|
DevOpsProjectLabelKey = "kubesphere.io/devopsproject"
|
||||||
|
KubefedManagedLabel = "kubefed.io/managed"
|
||||||
|
|
||||||
UserNameHeader = "X-Token-Username"
|
UserNameHeader = "X-Token-Username"
|
||||||
|
|
||||||
|
|||||||
@@ -24,14 +24,12 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/informers"
|
|
||||||
certificatesinformers "k8s.io/client-go/informers/certificates/v1beta1"
|
certificatesinformers "k8s.io/client-go/informers/certificates/v1beta1"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
certificateslisters "k8s.io/client-go/listers/certificates/v1beta1"
|
certificateslisters "k8s.io/client-go/listers/certificates/v1beta1"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
@@ -55,11 +53,7 @@ type Controller struct {
|
|||||||
csrInformer certificatesinformers.CertificateSigningRequestInformer
|
csrInformer certificatesinformers.CertificateSigningRequestInformer
|
||||||
csrLister certificateslisters.CertificateSigningRequestLister
|
csrLister certificateslisters.CertificateSigningRequestLister
|
||||||
csrSynced cache.InformerSynced
|
csrSynced cache.InformerSynced
|
||||||
|
|
||||||
cmInformer coreinformers.ConfigMapInformer
|
|
||||||
cmLister corelisters.ConfigMapLister
|
|
||||||
cmSynced cache.InformerSynced
|
cmSynced cache.InformerSynced
|
||||||
|
|
||||||
// workqueue is a rate limited work queue. This is used to queue work to be
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
// processed instead of performing it as soon as a change happens. This
|
// processed instead of performing it as soon as a change happens. This
|
||||||
// means we can ensure we only process a fixed amount of resources at a
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
@@ -72,7 +66,8 @@ type Controller struct {
|
|||||||
kubeconfigOperator kubeconfig.Interface
|
kubeconfigOperator kubeconfig.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController(k8sClient kubernetes.Interface, informerFactory informers.SharedInformerFactory, config *rest.Config) *Controller {
|
func NewController(k8sClient kubernetes.Interface, csrInformer certificatesinformers.CertificateSigningRequestInformer,
|
||||||
|
configMapInformer corev1informers.ConfigMapInformer, config *rest.Config) *Controller {
|
||||||
// Create event broadcaster
|
// Create event broadcaster
|
||||||
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
// logged for sample-controller types.
|
// logged for sample-controller types.
|
||||||
@@ -82,17 +77,13 @@ func NewController(k8sClient kubernetes.Interface, informerFactory informers.Sha
|
|||||||
eventBroadcaster.StartLogging(klog.Infof)
|
eventBroadcaster.StartLogging(klog.Infof)
|
||||||
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")})
|
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")})
|
||||||
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
csrInformer := informerFactory.Certificates().V1beta1().CertificateSigningRequests()
|
|
||||||
cmInformer := informerFactory.Core().V1().ConfigMaps()
|
|
||||||
ctl := &Controller{
|
ctl := &Controller{
|
||||||
k8sclient: k8sClient,
|
k8sclient: k8sClient,
|
||||||
csrInformer: csrInformer,
|
csrInformer: csrInformer,
|
||||||
csrLister: csrInformer.Lister(),
|
csrLister: csrInformer.Lister(),
|
||||||
csrSynced: csrInformer.Informer().HasSynced,
|
csrSynced: csrInformer.Informer().HasSynced,
|
||||||
cmInformer: cmInformer,
|
cmSynced: configMapInformer.Informer().HasSynced,
|
||||||
cmLister: cmInformer.Lister(),
|
kubeconfigOperator: kubeconfig.NewOperator(k8sClient, configMapInformer, config),
|
||||||
cmSynced: cmInformer.Informer().HasSynced,
|
|
||||||
kubeconfigOperator: kubeconfig.NewOperator(k8sClient, config, ""),
|
|
||||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "CertificateSigningRequest"),
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "CertificateSigningRequest"),
|
||||||
recorder: recorder,
|
recorder: recorder,
|
||||||
}
|
}
|
||||||
@@ -112,7 +103,7 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
|||||||
defer c.workqueue.ShutDown()
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
// Start the csrInformer factories to begin populating the csrInformer caches
|
// Start the csrInformer factories to begin populating the csrInformer caches
|
||||||
klog.Info("Starting User controller")
|
klog.Info("Starting CSR controller")
|
||||||
|
|
||||||
// Wait for the caches to be csrSynced before starting workers
|
// Wait for the caches to be csrSynced before starting workers
|
||||||
klog.Info("Waiting for csrInformer caches to sync")
|
klog.Info("Waiting for csrInformer caches to sync")
|
||||||
|
|||||||
@@ -100,10 +100,8 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
|||||||
defer utilruntime.HandleCrash()
|
defer utilruntime.HandleCrash()
|
||||||
defer c.workqueue.ShutDown()
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
//init client
|
|
||||||
|
|
||||||
// Start the informer factories to begin populating the informer caches
|
// Start the informer factories to begin populating the informer caches
|
||||||
klog.Info("Starting User controller")
|
klog.Info("Starting ClusterRoleBinding controller")
|
||||||
|
|
||||||
// Wait for the caches to be synced before starting workers
|
// Wait for the caches to be synced before starting workers
|
||||||
klog.Info("Waiting for informer caches to sync")
|
klog.Info("Waiting for informer caches to sync")
|
||||||
|
|||||||
354
pkg/controller/globalrole/globalrole_controller.go
Normal file
354
pkg/controller/globalrole/globalrole_controller.go
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package globalrole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
"k8s.io/klog"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
|
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
|
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
|
"reflect"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SuccessSynced is used as part of the Event 'reason' when a Foo is synced
|
||||||
|
successSynced = "Synced"
|
||||||
|
// is synced successfully
|
||||||
|
messageResourceSynced = "GlobalRole synced successfully"
|
||||||
|
controllerName = "globalrole-controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
k8sClient kubernetes.Interface
|
||||||
|
ksClient kubesphere.Interface
|
||||||
|
globalRoleInformer iamv1alpha2informers.GlobalRoleInformer
|
||||||
|
globalRoleLister iamv1alpha2listers.GlobalRoleLister
|
||||||
|
globalRoleSynced cache.InformerSynced
|
||||||
|
fedGlobalRoleCache cache.Store
|
||||||
|
fedGlobalRoleCacheController cache.Controller
|
||||||
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
|
// processed instead of performing it as soon as a change happens. This
|
||||||
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
|
// time, and makes it easy to ensure we are never processing the same item
|
||||||
|
// simultaneously in two different workers.
|
||||||
|
workqueue workqueue.RateLimitingInterface
|
||||||
|
// recorder is an event recorder for recording Event resources to the
|
||||||
|
// Kubernetes API.
|
||||||
|
recorder record.EventRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, globalRoleInformer iamv1alpha2informers.GlobalRoleInformer,
|
||||||
|
fedGlobalRoleCache cache.Store, fedGlobalRoleCacheController cache.Controller) *Controller {
|
||||||
|
// Create event broadcaster
|
||||||
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
|
// logged for sample-controller types.
|
||||||
|
|
||||||
|
klog.V(4).Info("Creating event broadcaster")
|
||||||
|
eventBroadcaster := record.NewBroadcaster()
|
||||||
|
eventBroadcaster.StartLogging(klog.Infof)
|
||||||
|
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")})
|
||||||
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
|
ctl := &Controller{
|
||||||
|
k8sClient: k8sClient,
|
||||||
|
ksClient: ksClient,
|
||||||
|
globalRoleInformer: globalRoleInformer,
|
||||||
|
globalRoleLister: globalRoleInformer.Lister(),
|
||||||
|
globalRoleSynced: globalRoleInformer.Informer().HasSynced,
|
||||||
|
fedGlobalRoleCache: fedGlobalRoleCache,
|
||||||
|
fedGlobalRoleCacheController: fedGlobalRoleCacheController,
|
||||||
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "GlobalRole"),
|
||||||
|
recorder: recorder,
|
||||||
|
}
|
||||||
|
klog.Info("Setting up event handlers")
|
||||||
|
globalRoleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: ctl.enqueueClusterRole,
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
ctl.enqueueClusterRole(new)
|
||||||
|
},
|
||||||
|
DeleteFunc: ctl.enqueueClusterRole,
|
||||||
|
})
|
||||||
|
return ctl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
||||||
|
defer utilruntime.HandleCrash()
|
||||||
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
|
// Start the informer factories to begin populating the informer caches
|
||||||
|
klog.Info("Starting GlobalRole controller")
|
||||||
|
|
||||||
|
// Wait for the caches to be synced before starting workers
|
||||||
|
klog.Info("Waiting for informer caches to sync")
|
||||||
|
|
||||||
|
if ok := cache.WaitForCacheSync(stopCh, c.globalRoleSynced, c.fedGlobalRoleCacheController.HasSynced); !ok {
|
||||||
|
return fmt.Errorf("failed to wait for caches to sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Starting workers")
|
||||||
|
// Launch two workers to process Foo resources
|
||||||
|
for i := 0; i < threadiness; i++ {
|
||||||
|
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Started workers")
|
||||||
|
<-stopCh
|
||||||
|
klog.Info("Shutting down workers")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) enqueueClusterRole(obj interface{}) {
|
||||||
|
var key string
|
||||||
|
var err error
|
||||||
|
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.workqueue.Add(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) runWorker() {
|
||||||
|
for c.processNextWorkItem() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) processNextWorkItem() bool {
|
||||||
|
obj, shutdown := c.workqueue.Get()
|
||||||
|
|
||||||
|
if shutdown {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// We wrap this block in a func so we can defer c.workqueue.Done.
|
||||||
|
err := func(obj interface{}) error {
|
||||||
|
// We call Done here so the workqueue knows we have finished
|
||||||
|
// processing this item. We also must remember to call Forget if we
|
||||||
|
// do not want this work item being re-queued. For example, we do
|
||||||
|
// not call Forget if a transient error occurs, instead the item is
|
||||||
|
// put back on the workqueue and attempted again after a back-off
|
||||||
|
// period.
|
||||||
|
defer c.workqueue.Done(obj)
|
||||||
|
var key string
|
||||||
|
var ok bool
|
||||||
|
// We expect strings to come off the workqueue. These are of the
|
||||||
|
// form namespace/name. We do this as the delayed nature of the
|
||||||
|
// workqueue means the items in the informer cache may actually be
|
||||||
|
// more up to date that when the item was initially put onto the
|
||||||
|
// workqueue.
|
||||||
|
if key, ok = obj.(string); !ok {
|
||||||
|
// As the item in the workqueue is actually invalid, we call
|
||||||
|
// Forget here else we'd go into a loop of attempting to
|
||||||
|
// process a work item that is invalid.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Run the reconcile, passing it the namespace/name string of the
|
||||||
|
// Foo resource to be synced.
|
||||||
|
if err := c.reconcile(key); err != nil {
|
||||||
|
// Put the item back on the workqueue to handle any transient errors.
|
||||||
|
c.workqueue.AddRateLimited(key)
|
||||||
|
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
|
||||||
|
}
|
||||||
|
// Finally, if no error occurs we Forget this item so it does not
|
||||||
|
// get queued again until another change happens.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
klog.Infof("Successfully synced %s:%s", "key", key)
|
||||||
|
return nil
|
||||||
|
}(obj)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncHandler compares the actual state with the desired, and attempts to
|
||||||
|
// converge the two. It then updates the Status block of the Foo resource
|
||||||
|
// with the current status of the resource.
|
||||||
|
func (c *Controller) reconcile(key string) error {
|
||||||
|
|
||||||
|
globalRole, err := c.globalRoleLister.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
// The user may no longer exist, in which case we stop
|
||||||
|
// processing.
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
utilruntime.HandleError(fmt.Errorf("globalrole '%s' in work queue no longer exists", key))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.multiClusterSync(globalRole); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.recorder.Event(globalRole, corev1.EventTypeNormal, successSynced, messageResourceSynced)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Start(stopCh <-chan struct{}) error {
|
||||||
|
return c.Run(4, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) multiClusterSync(globalRole *iamv1alpha2.GlobalRole) error {
|
||||||
|
|
||||||
|
if err := c.ensureNotControlledByKubefed(globalRole); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, exist, err := c.fedGlobalRoleCache.GetByKey(globalRole.Name)
|
||||||
|
if !exist {
|
||||||
|
return c.createFederatedGlobalRole(globalRole)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var federatedGlobalRole iamv1alpha2.FederatedRole
|
||||||
|
|
||||||
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(*unstructured.Unstructured).Object, &federatedGlobalRole); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(federatedGlobalRole.Spec.Template.Rules, globalRole.Rules) ||
|
||||||
|
!reflect.DeepEqual(federatedGlobalRole.Spec.Template.Labels, globalRole.Labels) ||
|
||||||
|
!reflect.DeepEqual(federatedGlobalRole.Spec.Template.Annotations, globalRole.Annotations) {
|
||||||
|
|
||||||
|
federatedGlobalRole.Spec.Template.Rules = globalRole.Rules
|
||||||
|
federatedGlobalRole.Spec.Template.Annotations = globalRole.Annotations
|
||||||
|
federatedGlobalRole.Spec.Template.Labels = globalRole.Labels
|
||||||
|
|
||||||
|
return c.updateFederatedGlobalRole(&federatedGlobalRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createFederatedGlobalRole(globalRole *iamv1alpha2.GlobalRole) error {
|
||||||
|
federatedGlobalRole := &iamv1alpha2.FederatedRole{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: iamv1alpha2.FedGlobalRoleKind,
|
||||||
|
APIVersion: iamv1alpha2.FedGlobalRoleResource.Group + "/" + iamv1alpha2.FedGlobalRoleResource.Version,
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: globalRole.Name,
|
||||||
|
},
|
||||||
|
Spec: iamv1alpha2.FederatedRoleSpec{
|
||||||
|
Template: iamv1alpha2.RoleTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: globalRole.Labels,
|
||||||
|
Annotations: globalRole.Annotations,
|
||||||
|
},
|
||||||
|
Rules: globalRole.Rules,
|
||||||
|
},
|
||||||
|
Placement: iamv1alpha2.Placement{
|
||||||
|
ClusterSelector: iamv1alpha2.ClusterSelector{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controllerutil.SetControllerReference(globalRole, federatedGlobalRole, scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedGlobalRole)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
err = cli.RESTClient().Post().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s", iamv1alpha2.FedGlobalRoleResource.Group,
|
||||||
|
iamv1alpha2.FedGlobalRoleResource.Version, iamv1alpha2.FedGlobalRoleResource.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateFederatedGlobalRole(federatedGlobalRole *iamv1alpha2.FederatedRole) error {
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedGlobalRole)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
err = cli.RESTClient().Put().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s/%s", iamv1alpha2.FedGlobalRoleResource.Group,
|
||||||
|
iamv1alpha2.FedGlobalRoleResource.Version, iamv1alpha2.FedGlobalRoleResource.Name,
|
||||||
|
federatedGlobalRole.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ensureNotControlledByKubefed(globalRole *iamv1alpha2.GlobalRole) error {
|
||||||
|
if globalRole.Labels[constants.KubefedManagedLabel] != "false" {
|
||||||
|
if globalRole.Labels == nil {
|
||||||
|
globalRole.Labels = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
globalRole = globalRole.DeepCopy()
|
||||||
|
globalRole.Labels[constants.KubefedManagedLabel] = "false"
|
||||||
|
_, err := c.ksClient.IamV1alpha2().GlobalRoles().Update(globalRole)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -23,6 +23,8 @@ import (
|
|||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@@ -33,8 +35,11 @@ import (
|
|||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
|
"reflect"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -45,16 +50,16 @@ const (
|
|||||||
// is synced successfully
|
// is synced successfully
|
||||||
messageResourceSynced = "GlobalRoleBinding synced successfully"
|
messageResourceSynced = "GlobalRoleBinding synced successfully"
|
||||||
controllerName = "globalrolebinding-controller"
|
controllerName = "globalrolebinding-controller"
|
||||||
federatedClusterRoleBindingKind = "FederatedClusterRoleBinding"
|
|
||||||
federatedResourceVersion = "types.kubefed.io/v1beta1"
|
|
||||||
federatedResourceAPIPath = "/apis/types.kubefed.io/v1beta1/federatedclusterrolebindings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
k8sClient kubernetes.Interface
|
k8sClient kubernetes.Interface
|
||||||
informer iamv1alpha2informers.GlobalRoleBindingInformer
|
ksClient kubesphere.Interface
|
||||||
lister iamv1alpha2listers.GlobalRoleBindingLister
|
globalRoleBindingInformer iamv1alpha2informers.GlobalRoleBindingInformer
|
||||||
synced cache.InformerSynced
|
globalRoleBindingLister iamv1alpha2listers.GlobalRoleBindingLister
|
||||||
|
globalRoleBindingSynced cache.InformerSynced
|
||||||
|
fedGlobalRoleBindingCache cache.Store
|
||||||
|
fedGlobalRoleBindingCacheController cache.Controller
|
||||||
// workqueue is a rate limited work queue. This is used to queue work to be
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
// processed instead of performing it as soon as a change happens. This
|
// processed instead of performing it as soon as a change happens. This
|
||||||
// means we can ensure we only process a fixed amount of resources at a
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
@@ -67,7 +72,8 @@ type Controller struct {
|
|||||||
multiClusterEnabled bool
|
multiClusterEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController(k8sClient kubernetes.Interface, globalRoleBindingInformer iamv1alpha2informers.GlobalRoleBindingInformer, multiClusterEnabled bool) *Controller {
|
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, globalRoleBindingInformer iamv1alpha2informers.GlobalRoleBindingInformer,
|
||||||
|
fedGlobalRoleBindingCache cache.Store, fedGlobalRoleBindingCacheController cache.Controller, multiClusterEnabled bool) *Controller {
|
||||||
// Create event broadcaster
|
// Create event broadcaster
|
||||||
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
// logged for sample-controller types.
|
// logged for sample-controller types.
|
||||||
@@ -79,10 +85,13 @@ func NewController(k8sClient kubernetes.Interface, globalRoleBindingInformer iam
|
|||||||
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
ctl := &Controller{
|
ctl := &Controller{
|
||||||
k8sClient: k8sClient,
|
k8sClient: k8sClient,
|
||||||
informer: globalRoleBindingInformer,
|
ksClient: ksClient,
|
||||||
lister: globalRoleBindingInformer.Lister(),
|
globalRoleBindingInformer: globalRoleBindingInformer,
|
||||||
synced: globalRoleBindingInformer.Informer().HasSynced,
|
globalRoleBindingLister: globalRoleBindingInformer.Lister(),
|
||||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ClusterRoleBinding"),
|
globalRoleBindingSynced: globalRoleBindingInformer.Informer().HasSynced,
|
||||||
|
fedGlobalRoleBindingCache: fedGlobalRoleBindingCache,
|
||||||
|
fedGlobalRoleBindingCacheController: fedGlobalRoleBindingCacheController,
|
||||||
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "GlobalRoleBinding"),
|
||||||
recorder: recorder,
|
recorder: recorder,
|
||||||
multiClusterEnabled: multiClusterEnabled,
|
multiClusterEnabled: multiClusterEnabled,
|
||||||
}
|
}
|
||||||
@@ -101,14 +110,19 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
|||||||
defer utilruntime.HandleCrash()
|
defer utilruntime.HandleCrash()
|
||||||
defer c.workqueue.ShutDown()
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
//init client
|
|
||||||
|
|
||||||
// Start the informer factories to begin populating the informer caches
|
// Start the informer factories to begin populating the informer caches
|
||||||
klog.Info("Starting User controller")
|
klog.Info("Starting GlobalRoleBinding controller")
|
||||||
|
|
||||||
// Wait for the caches to be synced before starting workers
|
// Wait for the caches to be synced before starting workers
|
||||||
klog.Info("Waiting for informer caches to sync")
|
klog.Info("Waiting for informer caches to sync")
|
||||||
if ok := cache.WaitForCacheSync(stopCh, c.synced); !ok {
|
|
||||||
|
synced := make([]cache.InformerSynced, 0)
|
||||||
|
synced = append(synced, c.globalRoleBindingSynced)
|
||||||
|
if c.multiClusterEnabled {
|
||||||
|
synced = append(synced, c.fedGlobalRoleBindingCacheController.HasSynced)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := cache.WaitForCacheSync(stopCh, synced...); !ok {
|
||||||
return fmt.Errorf("failed to wait for caches to sync")
|
return fmt.Errorf("failed to wait for caches to sync")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,12 +211,12 @@ func (c *Controller) processNextWorkItem() bool {
|
|||||||
// with the current status of the resource.
|
// with the current status of the resource.
|
||||||
func (c *Controller) reconcile(key string) error {
|
func (c *Controller) reconcile(key string) error {
|
||||||
|
|
||||||
globalRoleBinding, err := c.lister.Get(key)
|
globalRoleBinding, err := c.globalRoleBindingLister.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The user may no longer exist, in which case we stop
|
// The user may no longer exist, in which case we stop
|
||||||
// processing.
|
// processing.
|
||||||
if errors.IsNotFound(err) {
|
if errors.IsNotFound(err) {
|
||||||
utilruntime.HandleError(fmt.Errorf("clusterrolebinding '%s' in work queue no longer exists", key))
|
utilruntime.HandleError(fmt.Errorf("globalrolebinding '%s' in work queue no longer exists", key))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
@@ -216,6 +230,13 @@ func (c *Controller) reconcile(key string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.multiClusterEnabled {
|
||||||
|
if err = c.multiClusterSync(globalRoleBinding); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c.recorder.Event(globalRoleBinding, corev1.EventTypeNormal, successSynced, messageResourceSynced)
|
c.recorder.Event(globalRoleBinding, corev1.EventTypeNormal, successSynced, messageResourceSynced)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -224,64 +245,60 @@ func (c *Controller) Start(stopCh <-chan struct{}) error {
|
|||||||
return c.Run(4, stopCh)
|
return c.Run(4, stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) relateToClusterAdmin(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
|
func (c *Controller) multiClusterSync(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
|
||||||
|
|
||||||
if c.multiClusterEnabled {
|
if err := c.ensureNotControlledByKubefed(globalRoleBinding); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
federatedClusterRoleBinding := &iamv1alpha2.FederatedClusterRoleBinding{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
Kind: federatedClusterRoleBindingKind,
|
|
||||||
APIVersion: federatedResourceVersion,
|
|
||||||
},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: fmt.Sprintf("fed-%s", globalRoleBinding.Name),
|
|
||||||
},
|
|
||||||
Spec: iamv1alpha2.FederatedClusterRoleBindingSpec{
|
|
||||||
Template: iamv1alpha2.Template{
|
|
||||||
Subjects: ensureSubjectAPIVersionIsValid(globalRoleBinding.Subjects),
|
|
||||||
RoleRef: rbacv1.RoleRef{
|
|
||||||
APIGroup: "rbac.authorization.k8s.io",
|
|
||||||
Kind: iamv1alpha2.ResourceKindClusterRole,
|
|
||||||
Name: iamv1alpha2.ClusterAdmin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Placement: iamv1alpha2.Placement{
|
|
||||||
ClusterSelector: iamv1alpha2.ClusterSelector{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := controllerutil.SetControllerReference(globalRoleBinding, federatedClusterRoleBinding, scheme.Scheme)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(federatedClusterRoleBinding)
|
obj, exist, err := c.fedGlobalRoleBindingCache.GetByKey(globalRoleBinding.Name)
|
||||||
|
if !exist {
|
||||||
|
return c.createFederatedGlobalRoleBinding(globalRoleBinding)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cli := c.k8sClient.(*kubernetes.Clientset)
|
var federatedGlobalRoleBinding iamv1alpha2.FederatedRoleBinding
|
||||||
|
|
||||||
err = cli.RESTClient().Post().
|
err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(*unstructured.Unstructured).Object, &federatedGlobalRoleBinding)
|
||||||
AbsPath(federatedResourceAPIPath).
|
|
||||||
Body(data).
|
|
||||||
Do().Error()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsAlreadyExists(err) {
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(federatedGlobalRoleBinding.Spec.Template.Subjects, globalRoleBinding.Subjects) ||
|
||||||
|
!reflect.DeepEqual(federatedGlobalRoleBinding.Spec.Template.RoleRef, globalRoleBinding.RoleRef) ||
|
||||||
|
!reflect.DeepEqual(federatedGlobalRoleBinding.Spec.Template.Labels, globalRoleBinding.Labels) ||
|
||||||
|
!reflect.DeepEqual(federatedGlobalRoleBinding.Spec.Template.Annotations, globalRoleBinding.Annotations) {
|
||||||
|
|
||||||
|
federatedGlobalRoleBinding.Spec.Template.Subjects = globalRoleBinding.Subjects
|
||||||
|
federatedGlobalRoleBinding.Spec.Template.RoleRef = globalRoleBinding.RoleRef
|
||||||
|
federatedGlobalRoleBinding.Spec.Template.Annotations = globalRoleBinding.Annotations
|
||||||
|
federatedGlobalRoleBinding.Spec.Template.Labels = globalRoleBinding.Labels
|
||||||
|
|
||||||
|
return c.updateFederatedGlobalRoleBinding(&federatedGlobalRoleBinding)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
|
func (c *Controller) relateToClusterAdmin(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
|
||||||
|
|
||||||
|
username := findExpectUsername(globalRoleBinding)
|
||||||
|
|
||||||
|
// unexpected
|
||||||
|
if username == "" {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
|
||||||
clusterRoleBinding := &rbacv1.ClusterRoleBinding{
|
clusterRoleBinding := &rbacv1.ClusterRoleBinding{
|
||||||
TypeMeta: metav1.TypeMeta{},
|
TypeMeta: metav1.TypeMeta{},
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: fmt.Sprintf("fed-%s", globalRoleBinding.Name),
|
Name: fmt.Sprintf("%s-%s", username, iamv1alpha2.ClusterAdmin),
|
||||||
},
|
},
|
||||||
Subjects: ensureSubjectAPIVersionIsValid(globalRoleBinding.Subjects),
|
Subjects: ensureSubjectAPIVersionIsValid(globalRoleBinding.Subjects),
|
||||||
RoleRef: rbacv1.RoleRef{
|
RoleRef: rbacv1.RoleRef{
|
||||||
@@ -292,21 +309,117 @@ func (c *Controller) relateToClusterAdmin(globalRoleBinding *iamv1alpha2.GlobalR
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := controllerutil.SetControllerReference(globalRoleBinding, clusterRoleBinding, scheme.Scheme)
|
err := controllerutil.SetControllerReference(globalRoleBinding, clusterRoleBinding, scheme.Scheme)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.k8sClient.RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
|
_, err = c.k8sClient.RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsAlreadyExists(err) {
|
if errors.IsAlreadyExists(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findExpectUsername(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) string {
|
||||||
|
for _, subject := range globalRoleBinding.Subjects {
|
||||||
|
if subject.Kind == iamv1alpha2.ResourceKindUser {
|
||||||
|
return subject.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createFederatedGlobalRoleBinding(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
|
||||||
|
federatedGlobalRoleBinding := &iamv1alpha2.FederatedRoleBinding{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: iamv1alpha2.FedGlobalRoleBindingKind,
|
||||||
|
APIVersion: iamv1alpha2.FedGlobalRoleBindingResource.Group + "/" + iamv1alpha2.FedGlobalRoleBindingResource.Version,
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: globalRoleBinding.Name,
|
||||||
|
},
|
||||||
|
Spec: iamv1alpha2.FederatedRoleBindingSpec{
|
||||||
|
Template: iamv1alpha2.RoleBindingTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: globalRoleBinding.Labels,
|
||||||
|
Annotations: globalRoleBinding.Annotations,
|
||||||
|
},
|
||||||
|
Subjects: globalRoleBinding.Subjects,
|
||||||
|
RoleRef: globalRoleBinding.RoleRef,
|
||||||
|
},
|
||||||
|
Placement: iamv1alpha2.Placement{
|
||||||
|
ClusterSelector: iamv1alpha2.ClusterSelector{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controllerutil.SetControllerReference(globalRoleBinding, federatedGlobalRoleBinding, scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedGlobalRoleBinding)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
err = cli.RESTClient().Post().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s", iamv1alpha2.FedGlobalRoleBindingResource.Group,
|
||||||
|
iamv1alpha2.FedGlobalRoleBindingResource.Version, iamv1alpha2.FedGlobalRoleBindingResource.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateFederatedGlobalRoleBinding(federatedGlobalRoleBinding *iamv1alpha2.FederatedRoleBinding) error {
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedGlobalRoleBinding)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
err = cli.RESTClient().Put().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s/%s", iamv1alpha2.FedGlobalRoleBindingResource.Group,
|
||||||
|
iamv1alpha2.FedGlobalRoleBindingResource.Version, iamv1alpha2.FedGlobalRoleBindingResource.Name,
|
||||||
|
federatedGlobalRoleBinding.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ensureNotControlledByKubefed(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
|
||||||
|
if globalRoleBinding.Labels[constants.KubefedManagedLabel] != "false" {
|
||||||
|
if globalRoleBinding.Labels == nil {
|
||||||
|
globalRoleBinding.Labels = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
globalRoleBinding = globalRoleBinding.DeepCopy()
|
||||||
|
globalRoleBinding.Labels[constants.KubefedManagedLabel] = "false"
|
||||||
|
_, err := c.ksClient.IamV1alpha2().GlobalRoleBindings().Update(globalRoleBinding)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,17 +17,23 @@ limitations under the License.
|
|||||||
package namespace
|
package namespace
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/yaml"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
"kubesphere.io/kubesphere/pkg/constants"
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||||
|
"reflect"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
@@ -37,11 +43,6 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
|
|
||||||
* business logic. Delete these comments after modifying this file.*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Add creates a new Namespace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
// Add creates a new Namespace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
||||||
// and Start it when the Manager is Started.
|
// and Start it when the Manager is Started.
|
||||||
func Add(mgr manager.Manager) error {
|
func Add(mgr manager.Manager) error {
|
||||||
@@ -138,7 +139,15 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res
|
|||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = r.checkAndBindWorkspace(instance); err != nil {
|
if err = r.bindWorkspace(instance); err != nil {
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.initRoles(instance); err != nil {
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.initCreatorRoleBinding(instance); err != nil {
|
||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +166,7 @@ func (r *ReconcileNamespace) isControlledByWorkspace(namespace *corev1.Namespace
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileNamespace) checkAndBindWorkspace(namespace *corev1.Namespace) error {
|
func (r *ReconcileNamespace) bindWorkspace(namespace *corev1.Namespace) error {
|
||||||
|
|
||||||
workspaceName := namespace.Labels[constants.WorkspaceLabelKey]
|
workspaceName := namespace.Labels[constants.WorkspaceLabelKey]
|
||||||
|
|
||||||
@@ -165,7 +174,7 @@ func (r *ReconcileNamespace) checkAndBindWorkspace(namespace *corev1.Namespace)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace := &v1alpha1.Workspace{}
|
workspace := &tenantv1alpha1.Workspace{}
|
||||||
|
|
||||||
err := r.Get(context.TODO(), types.NamespacedName{Name: workspaceName}, workspace)
|
err := r.Get(context.TODO(), types.NamespacedName{Name: workspaceName}, workspace)
|
||||||
|
|
||||||
@@ -174,18 +183,20 @@ func (r *ReconcileNamespace) checkAndBindWorkspace(namespace *corev1.Namespace)
|
|||||||
if errors.IsNotFound(err) {
|
if errors.IsNotFound(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
klog.Errorf("bind workspace namespace: %s, workspace: %s, error: %s", namespace.Name, workspaceName, err)
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !metav1.IsControlledBy(namespace, workspace) {
|
// federated namespace not controlled by workspace
|
||||||
|
if namespace.Labels[constants.KubefedManagedLabel] != "true" && !metav1.IsControlledBy(namespace, workspace) {
|
||||||
|
namespace.OwnerReferences = removeWorkspaceOwnerReferences(namespace.OwnerReferences)
|
||||||
if err := controllerutil.SetControllerReference(workspace, namespace, r.scheme); err != nil {
|
if err := controllerutil.SetControllerReference(workspace, namespace, r.scheme); err != nil {
|
||||||
klog.Errorf("bind workspace namespace: %s, workspace: %s, error: %s", namespace.Name, workspaceName, err)
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = r.Update(context.TODO(), namespace)
|
err = r.Update(context.TODO(), namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("bind workspace namespace: %s, workspace: %s, error: %s", namespace.Name, workspaceName, err)
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,6 +204,16 @@ func (r *ReconcileNamespace) checkAndBindWorkspace(namespace *corev1.Namespace)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeWorkspaceOwnerReferences(ownerReferences []metav1.OwnerReference) []metav1.OwnerReference {
|
||||||
|
for i, owner := range ownerReferences {
|
||||||
|
if owner.Kind == tenantv1alpha1.ResourceKindWorkspace {
|
||||||
|
ownerReferences = append(ownerReferences[:i], ownerReferences[i+1:]...)
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ownerReferences
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ReconcileNamespace) deleteRouter(namespace string) error {
|
func (r *ReconcileNamespace) deleteRouter(namespace string) error {
|
||||||
routerName := constants.IngressControllerPrefix + namespace
|
routerName := constants.IngressControllerPrefix + namespace
|
||||||
|
|
||||||
@@ -230,5 +251,79 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReconcileNamespace) initRoles(namespace *corev1.Namespace) error {
|
||||||
|
var roleBases iamv1alpha2.RoleBaseList
|
||||||
|
|
||||||
|
err := r.List(context.Background(), &roleBases)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, roleBase := range roleBases.Items {
|
||||||
|
var role rbacv1.Role
|
||||||
|
|
||||||
|
if err = yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(roleBase.Role.Raw), 1024).Decode(&role); err == nil {
|
||||||
|
var old rbacv1.Role
|
||||||
|
err := r.Client.Get(context.Background(), types.NamespacedName{Namespace: namespace.Name, Name: role.Name}, &old)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
role.Namespace = namespace.Name
|
||||||
|
err = r.Client.Create(context.Background(), &role)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(role.Labels, old.Labels) ||
|
||||||
|
!reflect.DeepEqual(role.Annotations, old.Annotations) ||
|
||||||
|
!reflect.DeepEqual(role.Rules, old.Rules) {
|
||||||
|
|
||||||
|
old.Labels = role.Labels
|
||||||
|
old.Annotations = role.Annotations
|
||||||
|
old.Rules = role.Rules
|
||||||
|
|
||||||
|
return r.Update(context.Background(), &old)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReconcileNamespace) initCreatorRoleBinding(namespace *corev1.Namespace) error {
|
||||||
|
if creator := namespace.Annotations[constants.CreatorAnnotationKey]; creator != "" {
|
||||||
|
creatorRoleBinding := &rbacv1.RoleBinding{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: fmt.Sprintf("%s-%s", creator, iamv1alpha2.NamespaceAdmin),
|
||||||
|
Namespace: namespace.Name,
|
||||||
|
},
|
||||||
|
RoleRef: rbacv1.RoleRef{
|
||||||
|
APIGroup: rbacv1.GroupName,
|
||||||
|
Kind: iamv1alpha2.ResourceKindRole,
|
||||||
|
Name: iamv1alpha2.NamespaceAdmin,
|
||||||
|
},
|
||||||
|
Subjects: []rbacv1.Subject{
|
||||||
|
{
|
||||||
|
Name: creator,
|
||||||
|
Kind: iamv1alpha2.ResourceKindUser,
|
||||||
|
APIGroup: rbacv1.GroupName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := r.Client.Create(context.Background(), creatorRoleBinding)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,17 @@ limitations under the License.
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
@@ -36,7 +41,10 @@ import (
|
|||||||
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
|
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
|
||||||
userinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
userinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
userlister "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
userlister "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
|
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
|
||||||
|
"reflect"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -56,6 +64,9 @@ type Controller struct {
|
|||||||
userInformer userinformer.UserInformer
|
userInformer userinformer.UserInformer
|
||||||
userLister userlister.UserLister
|
userLister userlister.UserLister
|
||||||
userSynced cache.InformerSynced
|
userSynced cache.InformerSynced
|
||||||
|
cmSynced cache.InformerSynced
|
||||||
|
fedUserCache cache.Store
|
||||||
|
fedUserController cache.Controller
|
||||||
// workqueue is a rate limited work queue. This is used to queue work to be
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
// processed instead of performing it as soon as a change happens. This
|
// processed instead of performing it as soon as a change happens. This
|
||||||
// means we can ensure we only process a fixed amount of resources at a
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
@@ -65,10 +76,12 @@ type Controller struct {
|
|||||||
// recorder is an event recorder for recording Event resources to the
|
// recorder is an event recorder for recording Event resources to the
|
||||||
// Kubernetes API.
|
// Kubernetes API.
|
||||||
recorder record.EventRecorder
|
recorder record.EventRecorder
|
||||||
|
multiClusterEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface,
|
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface,
|
||||||
config *rest.Config, userInformer userinformer.UserInformer) *Controller {
|
config *rest.Config, userInformer userinformer.UserInformer, fedUserCache cache.Store, fedUserController cache.Controller,
|
||||||
|
configMapInformer corev1informers.ConfigMapInformer, multiClusterEnabled bool) *Controller {
|
||||||
// Create event broadcaster
|
// Create event broadcaster
|
||||||
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
// logged for sample-controller types.
|
// logged for sample-controller types.
|
||||||
@@ -81,7 +94,7 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
|
|||||||
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
var kubeconfigOperator kubeconfig.Interface
|
var kubeconfigOperator kubeconfig.Interface
|
||||||
if config != nil {
|
if config != nil {
|
||||||
kubeconfigOperator = kubeconfig.NewOperator(k8sClient, config, "")
|
kubeconfigOperator = kubeconfig.NewOperator(k8sClient, configMapInformer, config)
|
||||||
}
|
}
|
||||||
ctl := &Controller{
|
ctl := &Controller{
|
||||||
k8sClient: k8sClient,
|
k8sClient: k8sClient,
|
||||||
@@ -90,8 +103,12 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
|
|||||||
userInformer: userInformer,
|
userInformer: userInformer,
|
||||||
userLister: userInformer.Lister(),
|
userLister: userInformer.Lister(),
|
||||||
userSynced: userInformer.Informer().HasSynced,
|
userSynced: userInformer.Informer().HasSynced,
|
||||||
|
cmSynced: configMapInformer.Informer().HasSynced,
|
||||||
|
fedUserCache: fedUserCache,
|
||||||
|
fedUserController: fedUserController,
|
||||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Users"),
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Users"),
|
||||||
recorder: recorder,
|
recorder: recorder,
|
||||||
|
multiClusterEnabled: multiClusterEnabled,
|
||||||
}
|
}
|
||||||
klog.Info("Setting up event handlers")
|
klog.Info("Setting up event handlers")
|
||||||
userInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
userInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
@@ -108,14 +125,19 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
|||||||
defer utilruntime.HandleCrash()
|
defer utilruntime.HandleCrash()
|
||||||
defer c.workqueue.ShutDown()
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
//init client
|
|
||||||
|
|
||||||
// Start the informer factories to begin populating the informer caches
|
// Start the informer factories to begin populating the informer caches
|
||||||
klog.Info("Starting User controller")
|
klog.Info("Starting User controller")
|
||||||
|
|
||||||
// Wait for the caches to be synced before starting workers
|
// Wait for the caches to be synced before starting workers
|
||||||
klog.Info("Waiting for informer caches to sync")
|
klog.Info("Waiting for informer caches to sync")
|
||||||
if ok := cache.WaitForCacheSync(stopCh, c.userSynced); !ok {
|
|
||||||
|
synced := make([]cache.InformerSynced, 0)
|
||||||
|
synced = append(synced, c.userSynced, c.cmSynced)
|
||||||
|
if c.multiClusterEnabled {
|
||||||
|
synced = append(synced, c.fedUserController.HasSynced)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := cache.WaitForCacheSync(stopCh, synced...); !ok {
|
||||||
return fmt.Errorf("failed to wait for caches to sync")
|
return fmt.Errorf("failed to wait for caches to sync")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,16 +239,22 @@ func (c *Controller) reconcile(key string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = c.encryptPassword(user.DeepCopy())
|
if user, err = c.ensurePasswordIsEncrypted(user); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.kubeconfig != nil {
|
if c.kubeconfig != nil {
|
||||||
err = c.kubeconfig.CreateKubeConfig(user)
|
// ensure user kubeconfig configmap is created
|
||||||
if err != nil {
|
if err = c.kubeconfig.CreateKubeConfig(user); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// synchronization through kubefed-controller when multi cluster is enabled
|
||||||
|
if c.multiClusterEnabled {
|
||||||
|
if err = c.multiClusterSync(user); err != nil {
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -240,9 +268,8 @@ func (c *Controller) Start(stopCh <-chan struct{}) error {
|
|||||||
return c.Run(4, stopCh)
|
return c.Run(4, stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) encryptPassword(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
|
func (c *Controller) ensurePasswordIsEncrypted(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
|
||||||
encrypted, err := strconv.ParseBool(user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation])
|
encrypted, err := strconv.ParseBool(user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation])
|
||||||
|
|
||||||
// password is not encrypted
|
// password is not encrypted
|
||||||
if err != nil || !encrypted {
|
if err != nil || !encrypted {
|
||||||
password, err := encrypt(user.Spec.EncryptedPassword)
|
password, err := encrypt(user.Spec.EncryptedPassword)
|
||||||
@@ -250,21 +277,148 @@ func (c *Controller) encryptPassword(user *iamv1alpha2.User) (*iamv1alpha2.User,
|
|||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
user = user.DeepCopy()
|
||||||
user.Spec.EncryptedPassword = password
|
user.Spec.EncryptedPassword = password
|
||||||
if user.Annotations == nil {
|
if user.Annotations == nil {
|
||||||
user.Annotations = make(map[string]string, 0)
|
user.Annotations = make(map[string]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation] = "true"
|
user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation] = "true"
|
||||||
user.Status.State = iamv1alpha2.UserActive
|
user.Status.State = iamv1alpha2.UserActive
|
||||||
|
return c.ksClient.IamV1alpha2().Users().Update(user)
|
||||||
updated, err := c.ksClient.IamV1alpha2().Users().Update(user)
|
|
||||||
|
|
||||||
return updated, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ensureNotControlledByKubefed(user *iamv1alpha2.User) error {
|
||||||
|
if user.Labels[constants.KubefedManagedLabel] != "false" {
|
||||||
|
if user.Labels == nil {
|
||||||
|
user.Labels = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
user = user.DeepCopy()
|
||||||
|
user.Labels[constants.KubefedManagedLabel] = "false"
|
||||||
|
_, err := c.ksClient.IamV1alpha2().Users().Update(user)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) multiClusterSync(user *iamv1alpha2.User) error {
|
||||||
|
|
||||||
|
if err := c.ensureNotControlledByKubefed(user); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, exist, err := c.fedUserCache.GetByKey(user.Name)
|
||||||
|
if !exist {
|
||||||
|
return c.createFederatedUser(user)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var federatedUser iamv1alpha2.FederatedUser
|
||||||
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(*unstructured.Unstructured).Object, &federatedUser); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(federatedUser.Spec.Template.Spec, user.Spec) ||
|
||||||
|
!reflect.DeepEqual(federatedUser.Spec.Template.Status, user.Status) ||
|
||||||
|
!reflect.DeepEqual(federatedUser.Labels, user.Labels) ||
|
||||||
|
!reflect.DeepEqual(federatedUser.Annotations, user.Annotations) {
|
||||||
|
|
||||||
|
federatedUser.Labels = user.Labels
|
||||||
|
federatedUser.Spec.Template.Spec = user.Spec
|
||||||
|
federatedUser.Spec.Template.Status = user.Status
|
||||||
|
federatedUser.Spec.Template.Labels = user.Labels
|
||||||
|
federatedUser.Spec.Template.Annotations = user.Annotations
|
||||||
|
return c.updateFederatedUser(&federatedUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createFederatedUser(user *iamv1alpha2.User) error {
|
||||||
|
federatedUser := &iamv1alpha2.FederatedUser{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: iamv1alpha2.FedUserKind,
|
||||||
|
APIVersion: iamv1alpha2.FedUserResource.Group + "/" + iamv1alpha2.FedUserResource.Version,
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: user.Name,
|
||||||
|
},
|
||||||
|
Spec: iamv1alpha2.FederatedUserSpec{
|
||||||
|
Template: iamv1alpha2.UserTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: user.Labels,
|
||||||
|
Annotations: user.Annotations,
|
||||||
|
},
|
||||||
|
Spec: user.Spec,
|
||||||
|
Status: user.Status,
|
||||||
|
},
|
||||||
|
Placement: iamv1alpha2.Placement{
|
||||||
|
ClusterSelector: iamv1alpha2.ClusterSelector{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// must bind user lifecycle
|
||||||
|
err := controllerutil.SetControllerReference(user, federatedUser, scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
err = cli.RESTClient().Post().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s", iamv1alpha2.FedUserResource.Group,
|
||||||
|
iamv1alpha2.FedUserResource.Version, iamv1alpha2.FedUserResource.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateFederatedUser(fedUser *iamv1alpha2.FederatedUser) error {
|
||||||
|
data, err := json.Marshal(fedUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
err = cli.RESTClient().Put().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s/%s", iamv1alpha2.FedUserResource.Group,
|
||||||
|
iamv1alpha2.FedUserResource.Version, iamv1alpha2.FedUserResource.Name, fedUser.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func encrypt(password string) (string, error) {
|
func encrypt(password string) (string, error) {
|
||||||
// when user is already mapped to another identity, password is empty by default
|
// when user is already mapped to another identity, password is empty by default
|
||||||
// unable to log in directly until password reset
|
// unable to log in directly until password reset
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ func (f *fixture) newController() (*Controller, ksinformers.SharedInformerFactor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c := NewController(f.k8sclient, f.ksclient, nil, ksinformers.Iam().V1alpha2().Users())
|
c := NewController(f.k8sclient, f.ksclient, nil, ksinformers.Iam().V1alpha2().Users(), nil, nil, k8sinformers.Core().V1().ConfigMaps(), false)
|
||||||
c.userSynced = alwaysReady
|
c.userSynced = alwaysReady
|
||||||
c.recorder = &record.FakeRecorder{}
|
c.recorder = &record.FakeRecorder{}
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,19 @@ limitations under the License.
|
|||||||
package workspace
|
package workspace
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/yaml"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
|
"k8s.io/klog"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||||
|
"reflect"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||||
@@ -117,5 +124,48 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res
|
|||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = r.initRoles(instance); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return reconcile.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ReconcileWorkspace) initRoles(workspace *tenantv1alpha1.Workspace) error {
|
||||||
|
var roleBases iamv1alpha2.RoleBaseList
|
||||||
|
|
||||||
|
err := r.List(context.Background(), &roleBases)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, roleBase := range roleBases.Items {
|
||||||
|
var role iamv1alpha2.WorkspaceRole
|
||||||
|
|
||||||
|
if err = yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(roleBase.Role.Raw), 1024).Decode(&role); err == nil {
|
||||||
|
var old iamv1alpha2.WorkspaceRole
|
||||||
|
err := r.Client.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("%s-%s", workspace.Name, role.Name)}, &old)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
role.Name = fmt.Sprintf("%s-%s", workspace.Name, role.Name)
|
||||||
|
role.Labels[tenantv1alpha1.WorkspaceLabel] = workspace.Name
|
||||||
|
return r.Client.Create(context.Background(), &role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(role.Labels, old.Labels) ||
|
||||||
|
!reflect.DeepEqual(role.Annotations, old.Annotations) ||
|
||||||
|
!reflect.DeepEqual(role.Rules, old.Rules) {
|
||||||
|
|
||||||
|
old.Labels = role.Labels
|
||||||
|
old.Annotations = role.Annotations
|
||||||
|
old.Rules = role.Rules
|
||||||
|
|
||||||
|
return r.Update(context.Background(), &old)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
354
pkg/controller/workspacerole/workspacerole.go
Normal file
354
pkg/controller/workspacerole/workspacerole.go
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package workspacerole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
"k8s.io/klog"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
|
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
|
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
|
"reflect"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SuccessSynced is used as part of the Event 'reason' when a Foo is synced
|
||||||
|
successSynced = "Synced"
|
||||||
|
// is synced successfully
|
||||||
|
messageResourceSynced = "WorkspaceRole synced successfully"
|
||||||
|
controllerName = "workspacerole-controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
k8sClient kubernetes.Interface
|
||||||
|
ksClient kubesphere.Interface
|
||||||
|
workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer
|
||||||
|
workspaceRoleLister iamv1alpha2listers.WorkspaceRoleLister
|
||||||
|
workspaceRoleSynced cache.InformerSynced
|
||||||
|
fedWorkspaceRoleCache cache.Store
|
||||||
|
fedWorkspaceRoleCacheController cache.Controller
|
||||||
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
|
// processed instead of performing it as soon as a change happens. This
|
||||||
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
|
// time, and makes it easy to ensure we are never processing the same item
|
||||||
|
// simultaneously in two different workers.
|
||||||
|
workqueue workqueue.RateLimitingInterface
|
||||||
|
// recorder is an event recorder for recording Event resources to the
|
||||||
|
// Kubernetes API.
|
||||||
|
recorder record.EventRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer,
|
||||||
|
fedWorkspaceRoleCache cache.Store, fedWorkspaceRoleCacheController cache.Controller) *Controller {
|
||||||
|
// Create event broadcaster
|
||||||
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
|
// logged for sample-controller types.
|
||||||
|
|
||||||
|
klog.V(4).Info("Creating event broadcaster")
|
||||||
|
eventBroadcaster := record.NewBroadcaster()
|
||||||
|
eventBroadcaster.StartLogging(klog.Infof)
|
||||||
|
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")})
|
||||||
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
|
ctl := &Controller{
|
||||||
|
k8sClient: k8sClient,
|
||||||
|
ksClient: ksClient,
|
||||||
|
workspaceRoleInformer: workspaceRoleInformer,
|
||||||
|
workspaceRoleLister: workspaceRoleInformer.Lister(),
|
||||||
|
workspaceRoleSynced: workspaceRoleInformer.Informer().HasSynced,
|
||||||
|
fedWorkspaceRoleCache: fedWorkspaceRoleCache,
|
||||||
|
fedWorkspaceRoleCacheController: fedWorkspaceRoleCacheController,
|
||||||
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceRole"),
|
||||||
|
recorder: recorder,
|
||||||
|
}
|
||||||
|
klog.Info("Setting up event handlers")
|
||||||
|
workspaceRoleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: ctl.enqueueClusterRole,
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
ctl.enqueueClusterRole(new)
|
||||||
|
},
|
||||||
|
DeleteFunc: ctl.enqueueClusterRole,
|
||||||
|
})
|
||||||
|
return ctl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
||||||
|
defer utilruntime.HandleCrash()
|
||||||
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
|
// Start the informer factories to begin populating the informer caches
|
||||||
|
klog.Info("Starting GlobalRole controller")
|
||||||
|
|
||||||
|
// Wait for the caches to be synced before starting workers
|
||||||
|
klog.Info("Waiting for informer caches to sync")
|
||||||
|
|
||||||
|
if ok := cache.WaitForCacheSync(stopCh, c.workspaceRoleSynced, c.fedWorkspaceRoleCacheController.HasSynced); !ok {
|
||||||
|
return fmt.Errorf("failed to wait for caches to sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Starting workers")
|
||||||
|
// Launch two workers to process Foo resources
|
||||||
|
for i := 0; i < threadiness; i++ {
|
||||||
|
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Started workers")
|
||||||
|
<-stopCh
|
||||||
|
klog.Info("Shutting down workers")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) enqueueClusterRole(obj interface{}) {
|
||||||
|
var key string
|
||||||
|
var err error
|
||||||
|
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.workqueue.Add(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) runWorker() {
|
||||||
|
for c.processNextWorkItem() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) processNextWorkItem() bool {
|
||||||
|
obj, shutdown := c.workqueue.Get()
|
||||||
|
|
||||||
|
if shutdown {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// We wrap this block in a func so we can defer c.workqueue.Done.
|
||||||
|
err := func(obj interface{}) error {
|
||||||
|
// We call Done here so the workqueue knows we have finished
|
||||||
|
// processing this item. We also must remember to call Forget if we
|
||||||
|
// do not want this work item being re-queued. For example, we do
|
||||||
|
// not call Forget if a transient error occurs, instead the item is
|
||||||
|
// put back on the workqueue and attempted again after a back-off
|
||||||
|
// period.
|
||||||
|
defer c.workqueue.Done(obj)
|
||||||
|
var key string
|
||||||
|
var ok bool
|
||||||
|
// We expect strings to come off the workqueue. These are of the
|
||||||
|
// form namespace/name. We do this as the delayed nature of the
|
||||||
|
// workqueue means the items in the informer cache may actually be
|
||||||
|
// more up to date that when the item was initially put onto the
|
||||||
|
// workqueue.
|
||||||
|
if key, ok = obj.(string); !ok {
|
||||||
|
// As the item in the workqueue is actually invalid, we call
|
||||||
|
// Forget here else we'd go into a loop of attempting to
|
||||||
|
// process a work item that is invalid.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Run the reconcile, passing it the namespace/name string of the
|
||||||
|
// Foo resource to be synced.
|
||||||
|
if err := c.reconcile(key); err != nil {
|
||||||
|
// Put the item back on the workqueue to handle any transient errors.
|
||||||
|
c.workqueue.AddRateLimited(key)
|
||||||
|
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
|
||||||
|
}
|
||||||
|
// Finally, if no error occurs we Forget this item so it does not
|
||||||
|
// get queued again until another change happens.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
klog.Infof("Successfully synced %s:%s", "key", key)
|
||||||
|
return nil
|
||||||
|
}(obj)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncHandler compares the actual state with the desired, and attempts to
|
||||||
|
// converge the two. It then updates the Status block of the Foo resource
|
||||||
|
// with the current status of the resource.
|
||||||
|
func (c *Controller) reconcile(key string) error {
|
||||||
|
|
||||||
|
workspaceRole, err := c.workspaceRoleLister.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
// The user may no longer exist, in which case we stop
|
||||||
|
// processing.
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
utilruntime.HandleError(fmt.Errorf("workspacerole '%s' in work queue no longer exists", key))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.multiClusterSync(workspaceRole); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.recorder.Event(workspaceRole, corev1.EventTypeNormal, successSynced, messageResourceSynced)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Start(stopCh <-chan struct{}) error {
|
||||||
|
return c.Run(4, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) multiClusterSync(workspaceRole *iamv1alpha2.WorkspaceRole) error {
|
||||||
|
|
||||||
|
if err := c.ensureNotControlledByKubefed(workspaceRole); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, exist, err := c.fedWorkspaceRoleCache.GetByKey(workspaceRole.Name)
|
||||||
|
if !exist {
|
||||||
|
return c.createFederatedWorkspaceRole(workspaceRole)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var federatedWorkspaceRole iamv1alpha2.FederatedRole
|
||||||
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(*unstructured.Unstructured).Object, &federatedWorkspaceRole); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(federatedWorkspaceRole.Spec.Template.Rules, workspaceRole.Rules) ||
|
||||||
|
!reflect.DeepEqual(federatedWorkspaceRole.Spec.Template.Labels, workspaceRole.Labels) ||
|
||||||
|
!reflect.DeepEqual(federatedWorkspaceRole.Spec.Template.Annotations, workspaceRole.Annotations) {
|
||||||
|
|
||||||
|
federatedWorkspaceRole.Spec.Template.Rules = workspaceRole.Rules
|
||||||
|
federatedWorkspaceRole.Spec.Template.Annotations = workspaceRole.Annotations
|
||||||
|
federatedWorkspaceRole.Spec.Template.Labels = workspaceRole.Labels
|
||||||
|
|
||||||
|
return c.updateFederatedGlobalRole(&federatedWorkspaceRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createFederatedWorkspaceRole(workspaceRole *iamv1alpha2.WorkspaceRole) error {
|
||||||
|
federatedWorkspaceRole := &iamv1alpha2.FederatedRole{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: iamv1alpha2.FedWorkspaceRoleKind,
|
||||||
|
APIVersion: iamv1alpha2.FedWorkspaceRoleResource.Group + "/" + iamv1alpha2.FedWorkspaceRoleResource.Version,
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: workspaceRole.Name,
|
||||||
|
},
|
||||||
|
Spec: iamv1alpha2.FederatedRoleSpec{
|
||||||
|
Template: iamv1alpha2.RoleTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: workspaceRole.Labels,
|
||||||
|
Annotations: workspaceRole.Annotations,
|
||||||
|
},
|
||||||
|
Rules: workspaceRole.Rules,
|
||||||
|
},
|
||||||
|
Placement: iamv1alpha2.Placement{
|
||||||
|
ClusterSelector: iamv1alpha2.ClusterSelector{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controllerutil.SetControllerReference(workspaceRole, federatedWorkspaceRole, scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedWorkspaceRole)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
err = cli.RESTClient().Post().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s", iamv1alpha2.FedWorkspaceRoleResource.Group,
|
||||||
|
iamv1alpha2.FedWorkspaceRoleResource.Version, iamv1alpha2.FedWorkspaceRoleResource.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateFederatedGlobalRole(federatedWorkspaceRole *iamv1alpha2.FederatedRole) error {
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedWorkspaceRole)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
err = cli.RESTClient().Put().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s/%s", iamv1alpha2.FedWorkspaceRoleResource.Group,
|
||||||
|
iamv1alpha2.FedWorkspaceRoleResource.Version, iamv1alpha2.FedWorkspaceRoleResource.Name,
|
||||||
|
federatedWorkspaceRole.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ensureNotControlledByKubefed(workspaceRole *iamv1alpha2.WorkspaceRole) error {
|
||||||
|
if workspaceRole.Labels[constants.KubefedManagedLabel] != "false" {
|
||||||
|
if workspaceRole.Labels == nil {
|
||||||
|
workspaceRole.Labels = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
workspaceRole = workspaceRole.DeepCopy()
|
||||||
|
workspaceRole.Labels[constants.KubefedManagedLabel] = "false"
|
||||||
|
_, err := c.ksClient.IamV1alpha2().WorkspaceRoles().Update(workspaceRole)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,432 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package workspacerolebinding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
"k8s.io/klog"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
|
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
|
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
|
"reflect"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SuccessSynced is used as part of the Event 'reason' when a Foo is synced
|
||||||
|
successSynced = "Synced"
|
||||||
|
// is synced successfully
|
||||||
|
messageResourceSynced = "WorkspaceRoleBinding synced successfully"
|
||||||
|
controllerName = "workspacerolebinding-controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
k8sClient kubernetes.Interface
|
||||||
|
ksClient kubesphere.Interface
|
||||||
|
workspaceRoleBindingInformer iamv1alpha2informers.WorkspaceRoleBindingInformer
|
||||||
|
workspaceRoleBindingLister iamv1alpha2listers.WorkspaceRoleBindingLister
|
||||||
|
workspaceRoleBindingSynced cache.InformerSynced
|
||||||
|
fedWorkspaceRoleBindingCache cache.Store
|
||||||
|
fedWorkspaceRoleBindingCacheController cache.Controller
|
||||||
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
|
// processed instead of performing it as soon as a change happens. This
|
||||||
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
|
// time, and makes it easy to ensure we are never processing the same item
|
||||||
|
// simultaneously in two different workers.
|
||||||
|
workqueue workqueue.RateLimitingInterface
|
||||||
|
// recorder is an event recorder for recording Event resources to the
|
||||||
|
// Kubernetes API.
|
||||||
|
recorder record.EventRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, workspaceRoleBindingInformer iamv1alpha2informers.WorkspaceRoleBindingInformer,
|
||||||
|
fedWorkspaceRoleBindingCache cache.Store, fedWorkspaceRoleBindingCacheController cache.Controller) *Controller {
|
||||||
|
// Create event broadcaster
|
||||||
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
|
// logged for sample-controller types.
|
||||||
|
|
||||||
|
klog.V(4).Info("Creating event broadcaster")
|
||||||
|
eventBroadcaster := record.NewBroadcaster()
|
||||||
|
eventBroadcaster.StartLogging(klog.Infof)
|
||||||
|
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")})
|
||||||
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
|
ctl := &Controller{
|
||||||
|
k8sClient: k8sClient,
|
||||||
|
ksClient: ksClient,
|
||||||
|
workspaceRoleBindingInformer: workspaceRoleBindingInformer,
|
||||||
|
workspaceRoleBindingLister: workspaceRoleBindingInformer.Lister(),
|
||||||
|
workspaceRoleBindingSynced: workspaceRoleBindingInformer.Informer().HasSynced,
|
||||||
|
fedWorkspaceRoleBindingCache: fedWorkspaceRoleBindingCache,
|
||||||
|
fedWorkspaceRoleBindingCacheController: fedWorkspaceRoleBindingCacheController,
|
||||||
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceRoleBinding"),
|
||||||
|
recorder: recorder,
|
||||||
|
}
|
||||||
|
klog.Info("Setting up event handlers")
|
||||||
|
workspaceRoleBindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: ctl.enqueueClusterRoleBinding,
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
ctl.enqueueClusterRoleBinding(new)
|
||||||
|
},
|
||||||
|
DeleteFunc: ctl.enqueueClusterRoleBinding,
|
||||||
|
})
|
||||||
|
return ctl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
||||||
|
defer utilruntime.HandleCrash()
|
||||||
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
|
// Start the informer factories to begin populating the informer caches
|
||||||
|
klog.Info("Starting WorkspaceRoleBinding controller")
|
||||||
|
|
||||||
|
// Wait for the caches to be synced before starting workers
|
||||||
|
klog.Info("Waiting for informer caches to sync")
|
||||||
|
|
||||||
|
if ok := cache.WaitForCacheSync(stopCh, c.workspaceRoleBindingSynced, c.fedWorkspaceRoleBindingCacheController.HasSynced); !ok {
|
||||||
|
return fmt.Errorf("failed to wait for caches to sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Starting workers")
|
||||||
|
// Launch two workers to process Foo resources
|
||||||
|
for i := 0; i < threadiness; i++ {
|
||||||
|
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Started workers")
|
||||||
|
<-stopCh
|
||||||
|
klog.Info("Shutting down workers")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) enqueueClusterRoleBinding(obj interface{}) {
|
||||||
|
var key string
|
||||||
|
var err error
|
||||||
|
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.workqueue.Add(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) runWorker() {
|
||||||
|
for c.processNextWorkItem() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) processNextWorkItem() bool {
|
||||||
|
obj, shutdown := c.workqueue.Get()
|
||||||
|
|
||||||
|
if shutdown {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// We wrap this block in a func so we can defer c.workqueue.Done.
|
||||||
|
err := func(obj interface{}) error {
|
||||||
|
// We call Done here so the workqueue knows we have finished
|
||||||
|
// processing this item. We also must remember to call Forget if we
|
||||||
|
// do not want this work item being re-queued. For example, we do
|
||||||
|
// not call Forget if a transient error occurs, instead the item is
|
||||||
|
// put back on the workqueue and attempted again after a back-off
|
||||||
|
// period.
|
||||||
|
defer c.workqueue.Done(obj)
|
||||||
|
var key string
|
||||||
|
var ok bool
|
||||||
|
// We expect strings to come off the workqueue. These are of the
|
||||||
|
// form namespace/name. We do this as the delayed nature of the
|
||||||
|
// workqueue means the items in the informer cache may actually be
|
||||||
|
// more up to date that when the item was initially put onto the
|
||||||
|
// workqueue.
|
||||||
|
if key, ok = obj.(string); !ok {
|
||||||
|
// As the item in the workqueue is actually invalid, we call
|
||||||
|
// Forget here else we'd go into a loop of attempting to
|
||||||
|
// process a work item that is invalid.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Run the reconcile, passing it the namespace/name string of the
|
||||||
|
// Foo resource to be synced.
|
||||||
|
if err := c.reconcile(key); err != nil {
|
||||||
|
// Put the item back on the workqueue to handle any transient errors.
|
||||||
|
c.workqueue.AddRateLimited(key)
|
||||||
|
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
|
||||||
|
}
|
||||||
|
// Finally, if no error occurs we Forget this item so it does not
|
||||||
|
// get queued again until another change happens.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
klog.Infof("Successfully synced %s:%s", "key", key)
|
||||||
|
return nil
|
||||||
|
}(obj)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncHandler compares the actual state with the desired, and attempts to
|
||||||
|
// converge the two. It then updates the Status block of the Foo resource
|
||||||
|
// with the current status of the resource.
|
||||||
|
func (c *Controller) reconcile(key string) error {
|
||||||
|
|
||||||
|
workspaceRoleBinding, err := c.workspaceRoleBindingLister.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
// The user may no longer exist, in which case we stop
|
||||||
|
// processing.
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
utilruntime.HandleError(fmt.Errorf("workspacerolebinding '%s' in work queue no longer exists", key))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.multiClusterSync(workspaceRoleBinding); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.recorder.Event(workspaceRoleBinding, corev1.EventTypeNormal, successSynced, messageResourceSynced)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Start(stopCh <-chan struct{}) error {
|
||||||
|
return c.Run(4, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) multiClusterSync(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error {
|
||||||
|
|
||||||
|
if err := c.ensureNotControlledByKubefed(workspaceRoleBinding); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, exist, err := c.fedWorkspaceRoleBindingCache.GetByKey(workspaceRoleBinding.Name)
|
||||||
|
|
||||||
|
if !exist {
|
||||||
|
return c.createFederatedWorkspaceRoleBinding(workspaceRoleBinding)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var federatedWorkspaceRoleBinding iamv1alpha2.FederatedRoleBinding
|
||||||
|
|
||||||
|
err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(*unstructured.Unstructured).Object, &federatedWorkspaceRoleBinding)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(federatedWorkspaceRoleBinding.Spec.Template.Subjects, workspaceRoleBinding.Subjects) ||
|
||||||
|
!reflect.DeepEqual(federatedWorkspaceRoleBinding.Spec.Template.RoleRef, workspaceRoleBinding.RoleRef) ||
|
||||||
|
!reflect.DeepEqual(federatedWorkspaceRoleBinding.Spec.Template.Labels, workspaceRoleBinding.Labels) ||
|
||||||
|
!reflect.DeepEqual(federatedWorkspaceRoleBinding.Spec.Template.Annotations, workspaceRoleBinding.Annotations) {
|
||||||
|
|
||||||
|
federatedWorkspaceRoleBinding.Spec.Template.Subjects = workspaceRoleBinding.Subjects
|
||||||
|
federatedWorkspaceRoleBinding.Spec.Template.RoleRef = workspaceRoleBinding.RoleRef
|
||||||
|
federatedWorkspaceRoleBinding.Spec.Template.Annotations = workspaceRoleBinding.Annotations
|
||||||
|
federatedWorkspaceRoleBinding.Spec.Template.Labels = workspaceRoleBinding.Labels
|
||||||
|
|
||||||
|
return c.updateFederatedWorkspaceRoleBinding(&federatedWorkspaceRoleBinding)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) relateToClusterAdmin(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error {
|
||||||
|
|
||||||
|
username := findExpectUsername(workspaceRoleBinding)
|
||||||
|
|
||||||
|
// unexpected
|
||||||
|
if username == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterRoleBinding := &rbacv1.ClusterRoleBinding{
|
||||||
|
TypeMeta: metav1.TypeMeta{},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: fmt.Sprintf("%s-%s", username, iamv1alpha2.ClusterAdmin),
|
||||||
|
},
|
||||||
|
Subjects: ensureSubjectAPIVersionIsValid(workspaceRoleBinding.Subjects),
|
||||||
|
RoleRef: rbacv1.RoleRef{
|
||||||
|
APIGroup: "rbac.authorization.k8s.io",
|
||||||
|
Kind: iamv1alpha2.ResourceKindClusterRole,
|
||||||
|
Name: iamv1alpha2.ClusterAdmin,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controllerutil.SetControllerReference(workspaceRoleBinding, clusterRoleBinding, scheme.Scheme)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.k8sClient.RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// binding only one user is expected
|
||||||
|
func findExpectUsername(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) string {
|
||||||
|
for _, subject := range workspaceRoleBinding.Subjects {
|
||||||
|
if subject.Kind == iamv1alpha2.ResourceKindUser {
|
||||||
|
return subject.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createFederatedWorkspaceRoleBinding(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error {
|
||||||
|
federatedWorkspaceRoleBinding := &iamv1alpha2.FederatedRoleBinding{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: iamv1alpha2.FedWorkspaceRoleBindingKind,
|
||||||
|
APIVersion: iamv1alpha2.FedWorkspaceRoleBindingResource.Group + "/" + iamv1alpha2.FedWorkspaceRoleBindingResource.Version,
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: workspaceRoleBinding.Name,
|
||||||
|
},
|
||||||
|
Spec: iamv1alpha2.FederatedRoleBindingSpec{
|
||||||
|
Template: iamv1alpha2.RoleBindingTemplate{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: workspaceRoleBinding.Labels,
|
||||||
|
Annotations: workspaceRoleBinding.Annotations,
|
||||||
|
},
|
||||||
|
Subjects: workspaceRoleBinding.Subjects,
|
||||||
|
RoleRef: workspaceRoleBinding.RoleRef,
|
||||||
|
},
|
||||||
|
Placement: iamv1alpha2.Placement{
|
||||||
|
ClusterSelector: iamv1alpha2.ClusterSelector{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controllerutil.SetControllerReference(workspaceRoleBinding, federatedWorkspaceRoleBinding, scheme.Scheme)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedWorkspaceRoleBinding)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
err = cli.RESTClient().Post().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s", iamv1alpha2.FedWorkspaceRoleBindingResource.Group,
|
||||||
|
iamv1alpha2.FedWorkspaceRoleBindingResource.Version, iamv1alpha2.FedWorkspaceRoleBindingResource.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateFederatedWorkspaceRoleBinding(federatedWorkspaceRoleBinding *iamv1alpha2.FederatedRoleBinding) error {
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedWorkspaceRoleBinding)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
err = cli.RESTClient().Put().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s/%s", iamv1alpha2.FedWorkspaceRoleBindingResource.Group,
|
||||||
|
iamv1alpha2.FedWorkspaceRoleBindingResource.Version, iamv1alpha2.FedWorkspaceRoleBindingResource.Name,
|
||||||
|
federatedWorkspaceRoleBinding.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ensureNotControlledByKubefed(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error {
|
||||||
|
if workspaceRoleBinding.Labels[constants.KubefedManagedLabel] != "false" {
|
||||||
|
if workspaceRoleBinding.Labels == nil {
|
||||||
|
workspaceRoleBinding.Labels = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
|
||||||
|
workspaceRoleBinding.Labels[constants.KubefedManagedLabel] = "false"
|
||||||
|
_, err := c.ksClient.IamV1alpha2().WorkspaceRoleBindings().Update(workspaceRoleBinding)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureSubjectAPIVersionIsValid(subjects []rbacv1.Subject) []rbacv1.Subject {
|
||||||
|
validSubjects := make([]rbacv1.Subject, 0)
|
||||||
|
for _, subject := range subjects {
|
||||||
|
if subject.Kind == iamv1alpha2.ResourceKindUser {
|
||||||
|
validSubject := rbacv1.Subject{
|
||||||
|
Kind: iamv1alpha2.ResourceKindUser,
|
||||||
|
APIGroup: "rbac.authorization.k8s.io",
|
||||||
|
Name: subject.Name,
|
||||||
|
}
|
||||||
|
validSubjects = append(validSubjects, validSubject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validSubjects
|
||||||
|
}
|
||||||
494
pkg/controller/workspacetemplate/workspacetemplate_controller.go
Normal file
494
pkg/controller/workspacetemplate/workspacetemplate_controller.go
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The KubeSphere Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package workspacetemplate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/apimachinery/pkg/util/yaml"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
"k8s.io/klog"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
|
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
|
||||||
|
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
|
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
|
tenantv1alpha1informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
|
||||||
|
tenantv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha2"
|
||||||
|
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
tenantv1alpha1listers "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha1"
|
||||||
|
tenantv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha2"
|
||||||
|
"reflect"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SuccessSynced is used as part of the Event 'reason' when a Foo is synced
|
||||||
|
successSynced = "Synced"
|
||||||
|
// is synced successfully
|
||||||
|
messageResourceSynced = "WorkspaceTemplate synced successfully"
|
||||||
|
controllerName = "workspacetemplate-controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
k8sClient kubernetes.Interface
|
||||||
|
ksClient kubesphere.Interface
|
||||||
|
workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer
|
||||||
|
workspaceTemplateLister tenantv1alpha2listers.WorkspaceTemplateLister
|
||||||
|
workspaceTemplateSynced cache.InformerSynced
|
||||||
|
workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer
|
||||||
|
workspaceRoleLister iamv1alpha2listers.WorkspaceRoleLister
|
||||||
|
workspaceRoleSynced cache.InformerSynced
|
||||||
|
roleBaseInformer iamv1alpha2informers.RoleBaseInformer
|
||||||
|
roleBaseLister iamv1alpha2listers.RoleBaseLister
|
||||||
|
roleBaseSynced cache.InformerSynced
|
||||||
|
workspaceInformer tenantv1alpha1informers.WorkspaceInformer
|
||||||
|
workspaceLister tenantv1alpha1listers.WorkspaceLister
|
||||||
|
workspaceSynced cache.InformerSynced
|
||||||
|
fedWorkspaceCache cache.Store
|
||||||
|
fedWorkspaceCacheController cache.Controller
|
||||||
|
multiClusterEnabled bool
|
||||||
|
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||||
|
// processed instead of performing it as soon as a change happens. This
|
||||||
|
// means we can ensure we only process a fixed amount of resources at a
|
||||||
|
// time, and makes it easy to ensure we are never processing the same item
|
||||||
|
// simultaneously in two different workers.
|
||||||
|
workqueue workqueue.RateLimitingInterface
|
||||||
|
// recorder is an event recorder for recording Event resources to the
|
||||||
|
// Kubernetes API.
|
||||||
|
recorder record.EventRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer,
|
||||||
|
workspaceInformer tenantv1alpha1informers.WorkspaceInformer, roleBaseInformer iamv1alpha2informers.RoleBaseInformer, workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer,
|
||||||
|
fedWorkspaceCache cache.Store, fedWorkspaceCacheController cache.Controller, multiClusterEnabled bool) *Controller {
|
||||||
|
// Create event broadcaster
|
||||||
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
|
// logged for sample-controller types.
|
||||||
|
|
||||||
|
klog.V(4).Info("Creating event broadcaster")
|
||||||
|
eventBroadcaster := record.NewBroadcaster()
|
||||||
|
eventBroadcaster.StartLogging(klog.Infof)
|
||||||
|
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: k8sClient.CoreV1().Events("")})
|
||||||
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
|
ctl := &Controller{
|
||||||
|
k8sClient: k8sClient,
|
||||||
|
ksClient: ksClient,
|
||||||
|
workspaceTemplateInformer: workspaceTemplateInformer,
|
||||||
|
workspaceTemplateLister: workspaceTemplateInformer.Lister(),
|
||||||
|
workspaceTemplateSynced: workspaceTemplateInformer.Informer().HasSynced,
|
||||||
|
workspaceInformer: workspaceInformer,
|
||||||
|
workspaceLister: workspaceInformer.Lister(),
|
||||||
|
workspaceSynced: workspaceInformer.Informer().HasSynced,
|
||||||
|
workspaceRoleInformer: workspaceRoleInformer,
|
||||||
|
workspaceRoleLister: workspaceRoleInformer.Lister(),
|
||||||
|
workspaceRoleSynced: workspaceRoleInformer.Informer().HasSynced,
|
||||||
|
roleBaseInformer: roleBaseInformer,
|
||||||
|
roleBaseLister: roleBaseInformer.Lister(),
|
||||||
|
roleBaseSynced: roleBaseInformer.Informer().HasSynced,
|
||||||
|
fedWorkspaceCache: fedWorkspaceCache,
|
||||||
|
fedWorkspaceCacheController: fedWorkspaceCacheController,
|
||||||
|
multiClusterEnabled: multiClusterEnabled,
|
||||||
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceTemplate"),
|
||||||
|
recorder: recorder,
|
||||||
|
}
|
||||||
|
klog.Info("Setting up event handlers")
|
||||||
|
workspaceTemplateInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: ctl.enqueueClusterRole,
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
ctl.enqueueClusterRole(new)
|
||||||
|
},
|
||||||
|
DeleteFunc: ctl.enqueueClusterRole,
|
||||||
|
})
|
||||||
|
return ctl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
||||||
|
defer utilruntime.HandleCrash()
|
||||||
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
|
// Start the informer factories to begin populating the informer caches
|
||||||
|
klog.Info("Starting GlobalRole controller")
|
||||||
|
|
||||||
|
// Wait for the caches to be synced before starting workers
|
||||||
|
klog.Info("Waiting for informer caches to sync")
|
||||||
|
|
||||||
|
synced := make([]cache.InformerSynced, 0)
|
||||||
|
synced = append(synced, c.workspaceTemplateSynced, c.workspaceSynced, c.workspaceRoleSynced, c.roleBaseSynced)
|
||||||
|
if c.multiClusterEnabled {
|
||||||
|
synced = append(synced, c.fedWorkspaceCacheController.HasSynced)
|
||||||
|
}
|
||||||
|
if ok := cache.WaitForCacheSync(stopCh, synced...); !ok {
|
||||||
|
return fmt.Errorf("failed to wait for caches to sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Starting workers")
|
||||||
|
// Launch two workers to process Foo resources
|
||||||
|
for i := 0; i < threadiness; i++ {
|
||||||
|
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Info("Started workers")
|
||||||
|
<-stopCh
|
||||||
|
klog.Info("Shutting down workers")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) enqueueClusterRole(obj interface{}) {
|
||||||
|
var key string
|
||||||
|
var err error
|
||||||
|
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.workqueue.Add(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) runWorker() {
|
||||||
|
for c.processNextWorkItem() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) processNextWorkItem() bool {
|
||||||
|
obj, shutdown := c.workqueue.Get()
|
||||||
|
|
||||||
|
if shutdown {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// We wrap this block in a func so we can defer c.workqueue.Done.
|
||||||
|
err := func(obj interface{}) error {
|
||||||
|
// We call Done here so the workqueue knows we have finished
|
||||||
|
// processing this item. We also must remember to call Forget if we
|
||||||
|
// do not want this work item being re-queued. For example, we do
|
||||||
|
// not call Forget if a transient error occurs, instead the item is
|
||||||
|
// put back on the workqueue and attempted again after a back-off
|
||||||
|
// period.
|
||||||
|
defer c.workqueue.Done(obj)
|
||||||
|
var key string
|
||||||
|
var ok bool
|
||||||
|
// We expect strings to come off the workqueue. These are of the
|
||||||
|
// form namespace/name. We do this as the delayed nature of the
|
||||||
|
// workqueue means the items in the informer cache may actually be
|
||||||
|
// more up to date that when the item was initially put onto the
|
||||||
|
// workqueue.
|
||||||
|
if key, ok = obj.(string); !ok {
|
||||||
|
// As the item in the workqueue is actually invalid, we call
|
||||||
|
// Forget here else we'd go into a loop of attempting to
|
||||||
|
// process a work item that is invalid.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Run the reconcile, passing it the namespace/name string of the
|
||||||
|
// Foo resource to be synced.
|
||||||
|
if err := c.reconcile(key); err != nil {
|
||||||
|
// Put the item back on the workqueue to handle any transient errors.
|
||||||
|
c.workqueue.AddRateLimited(key)
|
||||||
|
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
|
||||||
|
}
|
||||||
|
// Finally, if no error occurs we Forget this item so it does not
|
||||||
|
// get queued again until another change happens.
|
||||||
|
c.workqueue.Forget(obj)
|
||||||
|
klog.Infof("Successfully synced %s:%s", "key", key)
|
||||||
|
return nil
|
||||||
|
}(obj)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncHandler compares the actual state with the desired, and attempts to
|
||||||
|
// converge the two. It then updates the Status block of the Foo resource
|
||||||
|
// with the current status of the resource.
|
||||||
|
func (c *Controller) reconcile(key string) error {
|
||||||
|
|
||||||
|
workspaceTemplate, err := c.workspaceTemplateLister.Get(key)
|
||||||
|
if err != nil {
|
||||||
|
// The user may no longer exist, in which case we stop
|
||||||
|
// processing.
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
utilruntime.HandleError(fmt.Errorf("workspace template '%s' in work queue no longer exists", key))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.initRoles(workspaceTemplate); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.multiClusterEnabled {
|
||||||
|
if err = c.multiClusterSync(workspaceTemplate); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = c.sync(workspaceTemplate); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.recorder.Event(workspaceTemplate, corev1.EventTypeNormal, successSynced, messageResourceSynced)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Start(stopCh <-chan struct{}) error {
|
||||||
|
return c.Run(4, stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) multiClusterSync(workspaceTemplate *tenantv1alpha2.WorkspaceTemplate) error {
|
||||||
|
|
||||||
|
obj, exist, err := c.fedWorkspaceCache.GetByKey(workspaceTemplate.Name)
|
||||||
|
if !exist {
|
||||||
|
return c.createFederatedWorkspace(workspaceTemplate)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var fedWorkspace tenantv1alpha2.FederatedWorkspace
|
||||||
|
|
||||||
|
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(*unstructured.Unstructured).Object, &fedWorkspace); err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(fedWorkspace.Spec.Template.Spec, workspaceTemplate.Spec.WorkspaceSpec) ||
|
||||||
|
!reflect.DeepEqual(fedWorkspace.Labels, workspaceTemplate.Labels) ||
|
||||||
|
!reflect.DeepEqual(fedWorkspace.Annotations, workspaceTemplate.Annotations) ||
|
||||||
|
!reflect.DeepEqual(fedWorkspace.Spec.Overrides, workspaceTemplate.Spec.Overrides) {
|
||||||
|
|
||||||
|
fedWorkspace.Spec.Template.Spec = workspaceTemplate.Spec.WorkspaceSpec
|
||||||
|
fedWorkspace.Annotations = workspaceTemplate.Annotations
|
||||||
|
fedWorkspace.Labels = workspaceTemplate.Labels
|
||||||
|
fedWorkspace.Spec.Overrides = workspaceTemplate.Spec.Overrides
|
||||||
|
|
||||||
|
return c.updateFederatedWorkspace(&fedWorkspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createFederatedWorkspace(workspaceTemplate *tenantv1alpha2.WorkspaceTemplate) error {
|
||||||
|
clusters := make([]tenantv1alpha2.Cluster, 0)
|
||||||
|
for _, cluster := range workspaceTemplate.Spec.Clusters {
|
||||||
|
clusters = append(clusters, tenantv1alpha2.Cluster{Name: cluster})
|
||||||
|
}
|
||||||
|
|
||||||
|
federatedWorkspace := &tenantv1alpha2.FederatedWorkspace{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: tenantv1alpha2.FedWorkspaceKind,
|
||||||
|
APIVersion: tenantv1alpha2.FedWorkspaceResource.Group + "/" + tenantv1alpha2.FedWorkspaceResource.Version,
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: workspaceTemplate.Name,
|
||||||
|
},
|
||||||
|
Spec: tenantv1alpha2.FederatedWorkspaceSpec{
|
||||||
|
Template: tenantv1alpha2.Template{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: workspaceTemplate.Labels,
|
||||||
|
Annotations: workspaceTemplate.Annotations,
|
||||||
|
},
|
||||||
|
Spec: workspaceTemplate.Spec.WorkspaceSpec,
|
||||||
|
},
|
||||||
|
Placement: tenantv1alpha2.Placement{
|
||||||
|
Clusters: clusters,
|
||||||
|
},
|
||||||
|
Overrides: workspaceTemplate.Spec.Overrides,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controllerutil.SetControllerReference(workspaceTemplate, federatedWorkspace, scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(federatedWorkspace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
err = cli.RESTClient().Post().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s", tenantv1alpha2.FedWorkspaceResource.Group,
|
||||||
|
tenantv1alpha2.FedWorkspaceResource.Version, tenantv1alpha2.FedWorkspaceResource.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateFederatedWorkspace(fedWorkspace *tenantv1alpha2.FederatedWorkspace) error {
|
||||||
|
|
||||||
|
data, err := json.Marshal(fedWorkspace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := c.k8sClient.(*kubernetes.Clientset)
|
||||||
|
err = cli.RESTClient().Put().
|
||||||
|
AbsPath(fmt.Sprintf("/apis/%s/%s/%s/%s", tenantv1alpha2.FedWorkspaceResource.Group,
|
||||||
|
tenantv1alpha2.FedWorkspaceResource.Version, tenantv1alpha2.FedWorkspaceResource.Name,
|
||||||
|
fedWorkspace.Name)).
|
||||||
|
Body(data).
|
||||||
|
Do().Error()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) sync(workspaceTemplate *tenantv1alpha2.WorkspaceTemplate) error {
|
||||||
|
workspace, err := c.workspaceLister.Get(workspaceTemplate.Name)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return c.createWorkspace(workspaceTemplate)
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(workspace.Spec, workspaceTemplate.Spec.WorkspaceSpec) ||
|
||||||
|
!reflect.DeepEqual(workspace.Labels, workspaceTemplate.Labels) ||
|
||||||
|
!reflect.DeepEqual(workspace.Annotations, workspaceTemplate.Annotations) {
|
||||||
|
|
||||||
|
workspace = workspace.DeepCopy()
|
||||||
|
workspace.Spec = workspaceTemplate.Spec.WorkspaceSpec
|
||||||
|
workspace.Annotations = workspaceTemplate.Annotations
|
||||||
|
workspace.Labels = workspaceTemplate.Labels
|
||||||
|
|
||||||
|
return c.updateWorkspace(workspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createWorkspace(workspaceTemplate *tenantv1alpha2.WorkspaceTemplate) error {
|
||||||
|
workspace := &tenantv1alpha1.Workspace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: workspaceTemplate.Name,
|
||||||
|
Labels: workspaceTemplate.Labels,
|
||||||
|
Annotations: workspaceTemplate.Annotations,
|
||||||
|
},
|
||||||
|
Spec: workspaceTemplate.Spec.WorkspaceSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controllerutil.SetControllerReference(workspaceTemplate, workspace, scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.ksClient.TenantV1alpha1().Workspaces().Create(workspace)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateWorkspace(workspace *tenantv1alpha1.Workspace) error {
|
||||||
|
_, err := c.ksClient.TenantV1alpha1().Workspaces().Update(workspace)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Controller) initRoles(workspace *tenantv1alpha2.WorkspaceTemplate) error {
|
||||||
|
roleBases, err := r.roleBaseLister.List(labels.Everything())
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, roleBase := range roleBases {
|
||||||
|
var role iamv1alpha2.WorkspaceRole
|
||||||
|
if err = yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(roleBase.Role.Raw), 1024).Decode(&role); err == nil {
|
||||||
|
old, err := r.workspaceRoleLister.Get(fmt.Sprintf("%s-%s", workspace.Name, role.Name))
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
role.Name = fmt.Sprintf("%s-%s", workspace.Name, role.Name)
|
||||||
|
if role.Labels == nil {
|
||||||
|
role.Labels = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
role.Labels[tenantv1alpha1.WorkspaceLabel] = workspace.Name
|
||||||
|
_, err = r.ksClient.IamV1alpha2().WorkspaceRoles().Create(&role)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(role.Annotations, old.Annotations) ||
|
||||||
|
!reflect.DeepEqual(role.Rules, old.Rules) {
|
||||||
|
updated := old.DeepCopy()
|
||||||
|
updated.Annotations = role.Annotations
|
||||||
|
updated.Rules = role.Rules
|
||||||
|
|
||||||
|
_, err = r.ksClient.IamV1alpha2().WorkspaceRoles().Update(updated)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ func newResourceHandler(k8sClient kubernetes.Interface, factory informers.Inform
|
|||||||
routerOperator: routers.NewRouterOperator(k8sClient, factory.KubernetesSharedInformerFactory()),
|
routerOperator: routers.NewRouterOperator(k8sClient, factory.KubernetesSharedInformerFactory()),
|
||||||
gitVerifier: git.NewGitVerifier(factory.KubernetesSharedInformerFactory()),
|
gitVerifier: git.NewGitVerifier(factory.KubernetesSharedInformerFactory()),
|
||||||
registryGetter: registries.NewRegistryGetter(factory.KubernetesSharedInformerFactory()),
|
registryGetter: registries.NewRegistryGetter(factory.KubernetesSharedInformerFactory()),
|
||||||
kubeconfigOperator: kubeconfig.NewOperator(k8sClient, nil, masterURL),
|
kubeconfigOperator: kubeconfig.NewReadOnlyOperator(factory.KubernetesSharedInformerFactory().Core().V1().ConfigMaps(), masterURL),
|
||||||
kubectlOperator: kubectl.NewOperator(nil, factory.KubernetesSharedInformerFactory().Apps().V1().Deployments(),
|
kubectlOperator: kubectl.NewOperator(nil, factory.KubernetesSharedInformerFactory().Apps().V1().Deployments(),
|
||||||
factory.KubernetesSharedInformerFactory().Core().V1().Pods(),
|
factory.KubernetesSharedInformerFactory().Core().V1().Pods(),
|
||||||
factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users()),
|
factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users()),
|
||||||
|
|||||||
@@ -457,3 +457,26 @@ func (h *tenantHandler) PatchWorkspace(request *restful.Request, response *restf
|
|||||||
|
|
||||||
response.WriteEntity(patched)
|
response.WriteEntity(patched)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *tenantHandler) ListClusters(r *restful.Request, response *restful.Response) {
|
||||||
|
user, ok := request.UserFrom(r.Request.Context())
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
response.WriteEntity([]interface{}{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := h.tenant.ListClusters(user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
api.HandleNotFound(response, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
api.HandleInternalError(response, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.WriteEntity(result)
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s
|
|||||||
ws := runtime.NewWebService(GroupVersion)
|
ws := runtime.NewWebService(GroupVersion)
|
||||||
handler := newTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient)
|
handler := newTenantHandler(factory, k8sclient, ksclient, evtsClient, loggingClient, auditingclient)
|
||||||
|
|
||||||
|
ws.Route(ws.GET("/clusters").
|
||||||
|
To(handler.ListClusters).
|
||||||
|
Doc("List clusters available to users").
|
||||||
|
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
|
||||||
|
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
|
||||||
ws.Route(ws.POST("/workspaces").
|
ws.Route(ws.POST("/workspaces").
|
||||||
To(handler.CreateWorkspace).
|
To(handler.CreateWorkspace).
|
||||||
Reads(tenantv1alpha2.WorkspaceTemplate{}).
|
Reads(tenantv1alpha2.WorkspaceTemplate{}).
|
||||||
@@ -82,12 +87,12 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s
|
|||||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
|
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
|
||||||
ws.Route(ws.GET("/workspaces/{workspace}").
|
ws.Route(ws.GET("/workspaces/{workspace}").
|
||||||
To(handler.DescribeWorkspace).
|
To(handler.DescribeWorkspace).
|
||||||
Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}).
|
Returns(http.StatusOK, api.StatusOK, tenantv1alpha2.WorkspaceTemplate{}).
|
||||||
Doc("Describe workspace.").
|
Doc("Describe workspace.").
|
||||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
|
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
|
||||||
ws.Route(ws.GET("/workspaces/{workspace}/clusters").
|
ws.Route(ws.GET("/workspaces/{workspace}/clusters").
|
||||||
To(handler.ListWorkspaceClusters).
|
To(handler.ListWorkspaceClusters).
|
||||||
Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}).
|
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
|
||||||
Doc("List clusters authorized to the specified workspace.").
|
Doc("List clusters authorized to the specified workspace.").
|
||||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
|
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
|
||||||
|
|
||||||
|
|||||||
@@ -333,9 +333,9 @@ func (am *amOperator) GetGlobalRole(globalRole string) (*iamv1alpha2.GlobalRole,
|
|||||||
return obj.(*iamv1alpha2.GlobalRole), nil
|
return obj.(*iamv1alpha2.GlobalRole), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *amOperator) CreateGlobalRoleBinding(username string, globalRole string) error {
|
func (am *amOperator) CreateGlobalRoleBinding(username string, role string) error {
|
||||||
|
|
||||||
_, err := am.GetGlobalRole(globalRole)
|
_, err := am.GetGlobalRole(role)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
@@ -350,7 +350,7 @@ func (am *amOperator) CreateGlobalRoleBinding(username string, globalRole string
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, roleBinding := range roleBindings {
|
for _, roleBinding := range roleBindings {
|
||||||
if globalRole == roleBinding.RoleRef.Name {
|
if role == roleBinding.RoleRef.Name {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := am.ksclient.IamV1alpha2().GlobalRoleBindings().Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
|
err := am.ksclient.IamV1alpha2().GlobalRoleBindings().Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
|
||||||
@@ -365,7 +365,7 @@ func (am *amOperator) CreateGlobalRoleBinding(username string, globalRole string
|
|||||||
|
|
||||||
globalRoleBinding := iamv1alpha2.GlobalRoleBinding{
|
globalRoleBinding := iamv1alpha2.GlobalRoleBinding{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: fmt.Sprintf("%s-%s", username, globalRole),
|
Name: fmt.Sprintf("%s-%s", username, role),
|
||||||
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username},
|
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username},
|
||||||
},
|
},
|
||||||
Subjects: []rbacv1.Subject{
|
Subjects: []rbacv1.Subject{
|
||||||
@@ -378,7 +378,7 @@ func (am *amOperator) CreateGlobalRoleBinding(username string, globalRole string
|
|||||||
RoleRef: rbacv1.RoleRef{
|
RoleRef: rbacv1.RoleRef{
|
||||||
APIGroup: iamv1alpha2.SchemeGroupVersion.Group,
|
APIGroup: iamv1alpha2.SchemeGroupVersion.Group,
|
||||||
Kind: iamv1alpha2.ResourceKindGlobalRole,
|
Kind: iamv1alpha2.ResourceKindGlobalRole,
|
||||||
Name: globalRole,
|
Name: role,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,7 +456,7 @@ func (am *amOperator) CreateWorkspaceRoleBinding(username string, workspace stri
|
|||||||
|
|
||||||
roleBinding := iamv1alpha2.WorkspaceRoleBinding{
|
roleBinding := iamv1alpha2.WorkspaceRoleBinding{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: fmt.Sprintf("%s-%s", role, username),
|
Name: fmt.Sprintf("%s-%s", username, role),
|
||||||
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username,
|
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username,
|
||||||
tenantv1alpha1.WorkspaceLabel: workspace},
|
tenantv1alpha1.WorkspaceLabel: workspace},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -131,10 +131,7 @@ func (im *defaultIMOperator) ListUsers(query *query.Query) (result *api.ListResu
|
|||||||
|
|
||||||
for _, item := range result.Items {
|
for _, item := range result.Items {
|
||||||
user := item.(*iamv1alpha2.User)
|
user := item.(*iamv1alpha2.User)
|
||||||
out := user.DeepCopy()
|
items = append(items, ensurePasswordNotOutput(user))
|
||||||
// ensure encrypted password will not be output
|
|
||||||
out.Spec.EncryptedPassword = ""
|
|
||||||
items = append(items, out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Items = items
|
result.Items = items
|
||||||
@@ -156,11 +153,8 @@ func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
user := obj.(*iamv1alpha2.User)
|
user := obj.(*iamv1alpha2.User)
|
||||||
out := user.DeepCopy()
|
|
||||||
// ensure encrypted password will not be output
|
|
||||||
out.Spec.EncryptedPassword = ""
|
|
||||||
|
|
||||||
return out, nil
|
return ensurePasswordNotOutput(user), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *defaultIMOperator) DeleteUser(username string) error {
|
func (im *defaultIMOperator) DeleteUser(username string) error {
|
||||||
@@ -175,3 +169,10 @@ func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.Us
|
|||||||
}
|
}
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensurePasswordNotOutput(user *iamv1alpha2.User) *iamv1alpha2.User {
|
||||||
|
out := user.DeepCopy()
|
||||||
|
// ensure encrypted password will not be output
|
||||||
|
out.Spec.EncryptedPassword = ""
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@@ -60,30 +61,38 @@ type Interface interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type operator struct {
|
type operator struct {
|
||||||
k8sclient kubernetes.Interface
|
k8sClient kubernetes.Interface
|
||||||
|
configMapInformer corev1informers.ConfigMapInformer
|
||||||
config *rest.Config
|
config *rest.Config
|
||||||
masterURL string
|
masterURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOperator(k8sclient kubernetes.Interface, config *rest.Config, masterURL string) Interface {
|
func NewOperator(k8sClient kubernetes.Interface, configMapInformer corev1informers.ConfigMapInformer, config *rest.Config) Interface {
|
||||||
return &operator{k8sclient: k8sclient, config: config, masterURL: masterURL}
|
return &operator{k8sClient: k8sClient, configMapInformer: configMapInformer, config: config}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReadOnlyOperator(configMapInformer corev1informers.ConfigMapInformer, masterURL string) Interface {
|
||||||
|
return &operator{configMapInformer: configMapInformer, masterURL: masterURL}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error {
|
func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error {
|
||||||
|
|
||||||
configName := fmt.Sprintf(kubeconfigNameFormat, user.Name)
|
configName := fmt.Sprintf(kubeconfigNameFormat, user.Name)
|
||||||
|
|
||||||
_, err := o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{})
|
_, err := o.configMapInformer.Lister().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName)
|
||||||
|
|
||||||
|
// already exist
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// internal error
|
||||||
if !errors.IsNotFound(err) {
|
if !errors.IsNotFound(err) {
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create if not exist
|
||||||
var ca []byte
|
var ca []byte
|
||||||
if len(o.config.CAData) > 0 {
|
if len(o.config.CAData) > 0 {
|
||||||
ca = o.config.CAData
|
ca = o.config.CAData
|
||||||
@@ -142,7 +151,7 @@ func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(cm)
|
_, err = o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(cm)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorln(err)
|
klog.Errorln(err)
|
||||||
@@ -154,7 +163,7 @@ func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error {
|
|||||||
|
|
||||||
func (o *operator) GetKubeConfig(username string) (string, error) {
|
func (o *operator) GetKubeConfig(username string) (string, error) {
|
||||||
configName := fmt.Sprintf(kubeconfigNameFormat, username)
|
configName := fmt.Sprintf(kubeconfigNameFormat, username)
|
||||||
configMap, err := o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{})
|
configMap, err := o.configMapInformer.Lister().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorln(err)
|
klog.Errorln(err)
|
||||||
return "", err
|
return "", err
|
||||||
@@ -171,6 +180,7 @@ func (o *operator) GetKubeConfig(username string) (string, error) {
|
|||||||
|
|
||||||
masterURL := o.masterURL
|
masterURL := o.masterURL
|
||||||
|
|
||||||
|
// server host override
|
||||||
if cluster := kubeconfig.Clusters[defaultClusterName]; cluster != nil {
|
if cluster := kubeconfig.Clusters[defaultClusterName]; cluster != nil {
|
||||||
cluster.Server = masterURL
|
cluster.Server = masterURL
|
||||||
}
|
}
|
||||||
@@ -244,7 +254,7 @@ func (o *operator) createCSR(username string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create csr
|
// create csr
|
||||||
k8sCSR, err = o.k8sclient.CertificatesV1beta1().CertificateSigningRequests().Create(k8sCSR)
|
k8sCSR, err = o.k8sClient.CertificatesV1beta1().CertificateSigningRequests().Create(k8sCSR)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorln(err)
|
klog.Errorln(err)
|
||||||
@@ -256,14 +266,14 @@ func (o *operator) createCSR(username string) ([]byte, error) {
|
|||||||
|
|
||||||
func (o *operator) UpdateKubeconfig(username string, certificate []byte) error {
|
func (o *operator) UpdateKubeconfig(username string, certificate []byte) error {
|
||||||
configName := fmt.Sprintf(kubeconfigNameFormat, username)
|
configName := fmt.Sprintf(kubeconfigNameFormat, username)
|
||||||
configMap, err := o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{})
|
configMap, err := o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorln(err)
|
klog.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
configMap = appendCert(configMap, certificate)
|
configMap = appendCert(configMap, certificate)
|
||||||
_, err = o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Update(configMap)
|
_, err = o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Update(configMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorln(err)
|
klog.Errorln(err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ type Interface interface {
|
|||||||
UpdateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
|
UpdateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
|
||||||
PatchNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
|
PatchNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
|
||||||
PatchWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error)
|
PatchWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error)
|
||||||
|
ListClusters(info user.Info) (*api.ListResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type tenantOperator struct {
|
type tenantOperator struct {
|
||||||
@@ -355,6 +356,90 @@ func (t *tenantOperator) ListWorkspaceClusters(workspaceName string) (*api.ListR
|
|||||||
}
|
}
|
||||||
return &api.ListResult{Items: clusters, TotalItems: len(clusters)}, nil
|
return &api.ListResult{Items: clusters, TotalItems: len(clusters)}, nil
|
||||||
}
|
}
|
||||||
|
func (t *tenantOperator) ListClusters(user user.Info) (*api.ListResult, error) {
|
||||||
|
|
||||||
|
listClustersInGlobalScope := authorizer.AttributesRecord{
|
||||||
|
User: user,
|
||||||
|
Verb: "list",
|
||||||
|
Resource: "clusters",
|
||||||
|
ResourceScope: request.GlobalScope,
|
||||||
|
ResourceRequest: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedListClustersInGlobalScope, _, err := t.authorizer.Authorize(listClustersInGlobalScope)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listWorkspacesInGlobalScope := authorizer.AttributesRecord{
|
||||||
|
User: user,
|
||||||
|
Verb: "list",
|
||||||
|
Resource: "workspaces",
|
||||||
|
ResourceScope: request.GlobalScope,
|
||||||
|
ResourceRequest: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedListWorkspacesInGlobalScope, _, err := t.authorizer.Authorize(listWorkspacesInGlobalScope)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowedListClustersInGlobalScope == authorizer.DecisionAllow ||
|
||||||
|
allowedListWorkspacesInGlobalScope == authorizer.DecisionAllow {
|
||||||
|
result, err := t.resourceGetter.List(clusterv1alpha1.ResourcesPluralCluster, "", query.New())
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaceRoleBindings, err := t.am.ListWorkspaceRoleBindings(user.GetName(), "")
|
||||||
|
|
||||||
|
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.DescribeWorkspace(workspaceName)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, clusterName := range workspace.Spec.Clusters {
|
||||||
|
// skip if cluster exist
|
||||||
|
if clusters[clusterName] != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj, err := t.resourceGetter.Get(clusterv1alpha1.ResourcesPluralCluster, "", clusterName)
|
||||||
|
if err != nil {
|
||||||
|
klog.Error(err)
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cluster := obj.(*clusterv1alpha1.Cluster)
|
||||||
|
clusters[clusterName] = cluster
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]interface{}, 0)
|
||||||
|
for _, cluster := range clusters {
|
||||||
|
items = append(items, cluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.ListResult{Items: items, TotalItems: len(items)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tenantOperator) DeleteWorkspace(workspace string) error {
|
func (t *tenantOperator) DeleteWorkspace(workspace string) error {
|
||||||
return t.ksclient.TenantV1alpha2().WorkspaceTemplates().Delete(workspace, metav1.NewDeleteOptions(0))
|
return t.ksclient.TenantV1alpha2().WorkspaceTemplates().Delete(workspace, metav1.NewDeleteOptions(0))
|
||||||
|
|||||||
Reference in New Issue
Block a user