提交 a900b6af 编写于 作者: H hongming

refactor workspace controller

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 ce0f4179
......@@ -45,9 +45,6 @@ import (
"kubesphere.io/kubesphere/pkg/controller/storage/expansion"
"kubesphere.io/kubesphere/pkg/controller/user"
"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/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
......@@ -164,10 +161,8 @@ func addControllers(
kubernetesInformer.Apps().V1().ReplicaSets(),
kubernetesInformer.Apps().V1().StatefulSets())
var fedUserCache, fedGlobalRoleBindingCache, fedGlobalRoleCache,
fedWorkspaceRoleCache, fedWorkspaceRoleBindingCache cache.Store
var fedUserCacheController, fedGlobalRoleBindingCacheController, fedGlobalRoleCacheController,
fedWorkspaceRoleCacheController, fedWorkspaceRoleBindingCacheController cache.Controller
var fedUserCache, fedGlobalRoleBindingCache, fedGlobalRoleCache cache.Store
var fedUserCacheController, fedGlobalRoleBindingCacheController, fedGlobalRoleCacheController cache.Controller
if multiClusterEnabled {
fedUserClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedUserResource)
......@@ -185,28 +180,14 @@ func addControllers(
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) {})
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 fedWorkspaceRoleCacheController.Run(stopCh)
go fedWorkspaceRoleBindingCacheController.Run(stopCh)
}
userController := user.NewUserController(client.Kubernetes(), client.KubeSphere(), client.Config(),
......@@ -238,29 +219,11 @@ func addControllers(
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,
kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(), multiClusterEnabled)
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,
kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(), multiClusterEnabled)
workspaceTemplateController := workspacetemplate.NewController(client.Kubernetes(), client.KubeSphere(),
kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(),
kubesphereInformer.Tenant().V1alpha1().Workspaces(),
kubesphereInformer.Iam().V1alpha2().RoleBases(),
kubesphereInformer.Iam().V1alpha2().WorkspaceRoles(),
kubesphereInformer.Types().V1beta1().FederatedWorkspaces(),
multiClusterEnabled)
groupBindingController := groupbinding.NewController(client.Kubernetes(), client.KubeSphere(),
kubesphereInformer.Iam().V1alpha2().GroupBindings(),
kubesphereInformer.Types().V1beta1().FederatedGroupBindings(),
......@@ -313,27 +276,24 @@ func addControllers(
}
controllers := map[string]manager.Runnable{
"virtualservice-controller": vsController,
"destinationrule-controller": drController,
"application-controller": apController,
"job-controller": jobController,
"s2ibinary-controller": s2iBinaryController,
"s2irun-controller": s2iRunController,
"storagecapability-controller": storageCapabilityController,
"volumeexpansion-controller": volumeExpansionController,
"user-controller": userController,
"loginrecord-controller": loginRecordController,
"cluster-controller": clusterController,
"nsnp-controller": nsnpController,
"csr-controller": csrController,
"clusterrolebinding-controller": clusterRoleBindingController,
"globalrolebinding-controller": globalRoleBindingController,
"workspacetemplate-controller": workspaceTemplateController,
"workspacerole-controller": workspaceRoleController,
"workspacerolebinding-controller": workspaceRoleBindingController,
"ippool-controller": ippoolController,
"groupbinding-controller": groupBindingController,
"group-controller": groupController,
"virtualservice-controller": vsController,
"destinationrule-controller": drController,
"application-controller": apController,
"job-controller": jobController,
"s2ibinary-controller": s2iBinaryController,
"s2irun-controller": s2iRunController,
"storagecapability-controller": storageCapabilityController,
"volumeexpansion-controller": volumeExpansionController,
"user-controller": userController,
"loginrecord-controller": loginRecordController,
"cluster-controller": clusterController,
"nsnp-controller": nsnpController,
"csr-controller": csrController,
"clusterrolebinding-controller": clusterRoleBindingController,
"globalrolebinding-controller": globalRoleBindingController,
"ippool-controller": ippoolController,
"groupbinding-controller": groupBindingController,
"group-controller": groupController,
}
if devopsClient != nil {
......
......@@ -18,8 +18,6 @@ package app
import (
"fmt"
"os"
"github.com/spf13/cobra"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
cliflag "k8s.io/component-base/cli/flag"
......@@ -32,6 +30,9 @@ import (
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
"kubesphere.io/kubesphere/pkg/controller/user"
"kubesphere.io/kubesphere/pkg/controller/workspace"
"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/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
......@@ -40,7 +41,9 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"kubesphere.io/kubesphere/pkg/utils/term"
"os"
application "sigs.k8s.io/application/controllers"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
"sigs.k8s.io/controller-runtime/pkg/webhook"
......@@ -118,7 +121,7 @@ func run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
}
var ldapClient ldapclient.Interface
// when there is no ldapOption, we set ldapClient as nil, which means we don't need to sync user info into ldap.
//when there is no ldapOption, we set ldapClient as nil, which means we don't need to sync user info into ldap.
if s.LdapOptions != nil && len(s.LdapOptions.Host) != 0 {
if s.LdapOptions.Host == ldapclient.FAKE_HOST { // for debug only
ldapClient = ldapclient.NewSimpleLdap()
......@@ -175,7 +178,7 @@ func run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
}
klog.V(0).Info("setting up manager")
ctrl.SetLogger(klogr.New())
// Use 8443 instead of 443 cause we need root permission to bind port 443
mgr, err := manager.New(kubernetesClient.Config(), mgrOptions)
if err != nil {
......@@ -186,23 +189,38 @@ func run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
klog.Fatalf("unable add APIs to scheme: %v", err)
}
err = workspace.Add(mgr)
if err != nil {
workspaceTemplateReconciler := &workspacetemplate.Reconciler{MultiClusterEnabled: s.MultiClusterOptions.Enable}
if err = workspaceTemplateReconciler.SetupWithManager(mgr); err != nil {
klog.Fatal("Unable to create workspace template controller")
}
workspaceReconciler := &workspace.Reconciler{}
if err = workspaceReconciler.SetupWithManager(mgr); err != nil {
klog.Fatal("Unable to create workspace controller")
}
err = namespace.Add(mgr)
if err != nil {
workspaceRoleReconciler := &workspacerole.Reconciler{MultiClusterEnabled: s.MultiClusterOptions.Enable}
if err = workspaceRoleReconciler.SetupWithManager(mgr); err != nil {
klog.Fatal("Unable to create workspace role controller")
}
workspaceRoleBindingReconciler := &workspacerolebinding.Reconciler{MultiClusterEnabled: s.MultiClusterOptions.Enable}
if err = workspaceRoleBindingReconciler.SetupWithManager(mgr); err != nil {
klog.Fatal("Unable to create workspace role binding controller")
}
namespaceReconciler := &namespace.Reconciler{}
if err = namespaceReconciler.SetupWithManager(mgr); err != nil {
klog.Fatal("Unable to create namespace controller")
}
err = (&application.ApplicationReconciler{
applicationReconciler := &application.ApplicationReconciler{
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Mapper: mgr.GetRESTMapper(),
Log: klogr.New(),
}).SetupWithManager(mgr)
if err != nil {
}
if err = applicationReconciler.SetupWithManager(mgr); err != nil {
klog.Fatal("Unable to create application controller")
}
......
......@@ -28,6 +28,7 @@ require (
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/structs v1.1.0
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.1 // indirect
github.com/go-openapi/loads v0.19.5
github.com/go-openapi/spec v0.19.7
......
......@@ -276,6 +276,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
......@@ -583,6 +584,7 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.3.2/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
......@@ -615,6 +617,7 @@ gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
......
......@@ -81,7 +81,6 @@ const (
ScopeDevOps = "devops"
PlatformAdmin = "platform-admin"
NamespaceAdmin = "admin"
WorkspaceAdminFormat = "%s-admin"
ClusterAdmin = "cluster-admin"
PreRegistrationUser = "system:pre-registration"
PreRegistrationUserGroup = "pre-registration"
......
......@@ -27,7 +27,6 @@ const (
FederatedUserKind = "FederatedUser"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:openapi-gen=true
type FederatedUser struct {
......@@ -50,7 +49,7 @@ type UserTemplate struct {
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FederatedUserList contains a list of federateduserlists
// FederatedUserList contains a list of FederatedUser
type FederatedUserList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
......
......@@ -27,10 +27,7 @@ const (
FederatedWorkspaceKind = "FederatedWorkspace"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient:nonNamespaced
// +k8s:openapi-gen=true
type FederatedWorkspace struct {
metav1.TypeMeta `json:",inline"`
......@@ -53,7 +50,7 @@ type WorkspaceTemplate struct {
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FederatedWorkspaceList contains a list of federatedworkspacelists
// FederatedWorkspaceList contains a list of FederatedWorkspace
type FederatedWorkspaceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
......
/*
Copyright 2020 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 v1beta1
import (
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
ResourcePluralFederatedWorkspaceRole = "federatedworkspaceroles"
ResourceSingularFederatedWorkspaceRole = "federatedworkspacerole"
FederatedWorkspaceRoleKind = "FederatedWorkspaceRole"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:openapi-gen=true
type FederatedWorkspaceRole struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec FederatedWorkspaceRoleSpec `json:"spec"`
Status *GenericFederatedStatus `json:"status,omitempty"`
}
type FederatedWorkspaceRoleSpec struct {
Template WorkspaceRoleTemplate `json:"template"`
Placement GenericPlacementFields `json:"placement"`
Overrides []GenericOverrideItem `json:"overrides,omitempty"`
}
type WorkspaceRoleTemplate struct {
metav1.ObjectMeta `json:"metadata,omitempty"`
// Rules holds all the PolicyRules for this WorkspaceRole
// +optional
Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FederatedWorkspaceRoleList contains a list of FederatedWorkspaceRole
type FederatedWorkspaceRoleList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []FederatedWorkspaceRole `json:"items"`
}
/*
Copyright 2020 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 v1beta1
import (
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
ResourcePluralFederatedWorkspaceRoleBinding = "federatedworkspacerolebindings"
ResourceSingularFederatedWorkspaceRoleBinding = "federatedworkspacerolebinding"
FederatedWorkspaceRoleBindingKind = "FederatedWorkspaceRoleBinding"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:openapi-gen=true
type FederatedWorkspaceRoleBinding struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec FederatedWorkspaceRoleBindingSpec `json:"spec"`
Status *GenericFederatedStatus `json:"status,omitempty"`
}
type FederatedWorkspaceRoleBindingSpec struct {
Template WorkspaceRoleBindingTemplate `json:"template"`
Placement GenericPlacementFields `json:"placement"`
Overrides []GenericOverrideItem `json:"overrides,omitempty"`
}
type WorkspaceRoleBindingTemplate struct {
metav1.ObjectMeta `json:"metadata,omitempty"`
// Subjects holds references to the objects the role applies to.
// +optional
Subjects []rbacv1.Subject `json:"subjects,omitempty" protobuf:"bytes,2,rep,name=subjects"`
// RoleRef can only reference a WorkspaceRole.
// If the RoleRef cannot be resolved, the Authorizer must return an error.
RoleRef rbacv1.RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FederatedWorkspaceRoleBindingList contains a list of FederatedWorkspaceRoleBinding
type FederatedWorkspaceRoleBindingList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []FederatedWorkspaceRoleBinding `json:"items"`
}
......@@ -77,5 +77,9 @@ func init() {
&FederatedGroup{},
&FederatedGroupList{},
&FederatedWorkspace{},
&FederatedWorkspaceList{})
&FederatedWorkspaceList{},
&FederatedWorkspaceRole{},
&FederatedWorkspaceRoleList{},
&FederatedWorkspaceRoleBinding{},
&FederatedWorkspaceRoleBindingList{})
}
......@@ -773,7 +773,7 @@ func (in *FederatedGroupList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederatedGroupSpec) DeepCopyInto(out *FederatedGroupSpec) {
*out = *in
out.Template = in.Template
in.Template.DeepCopyInto(&out.Template)
in.Placement.DeepCopyInto(&out.Placement)
if in.Overrides != nil {
in, out := &in.Overrides, &out.Overrides
......@@ -1760,6 +1760,209 @@ func (in *FederatedWorkspaceList) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederatedWorkspaceRole) DeepCopyInto(out *FederatedWorkspaceRole) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
if in.Status != nil {
in, out := &in.Status, &out.Status
*out = new(GenericFederatedStatus)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRole.
func (in *FederatedWorkspaceRole) DeepCopy() *FederatedWorkspaceRole {
if in == nil {
return nil
}
out := new(FederatedWorkspaceRole)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedWorkspaceRole) 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 *FederatedWorkspaceRoleBinding) DeepCopyInto(out *FederatedWorkspaceRoleBinding) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
if in.Status != nil {
in, out := &in.Status, &out.Status
*out = new(GenericFederatedStatus)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleBinding.
func (in *FederatedWorkspaceRoleBinding) DeepCopy() *FederatedWorkspaceRoleBinding {
if in == nil {
return nil
}
out := new(FederatedWorkspaceRoleBinding)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedWorkspaceRoleBinding) 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 *FederatedWorkspaceRoleBindingList) DeepCopyInto(out *FederatedWorkspaceRoleBindingList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]FederatedWorkspaceRoleBinding, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleBindingList.
func (in *FederatedWorkspaceRoleBindingList) DeepCopy() *FederatedWorkspaceRoleBindingList {
if in == nil {
return nil
}
out := new(FederatedWorkspaceRoleBindingList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedWorkspaceRoleBindingList) 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 *FederatedWorkspaceRoleBindingSpec) DeepCopyInto(out *FederatedWorkspaceRoleBindingSpec) {
*out = *in
in.Template.DeepCopyInto(&out.Template)
in.Placement.DeepCopyInto(&out.Placement)
if in.Overrides != nil {
in, out := &in.Overrides, &out.Overrides
*out = make([]GenericOverrideItem, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleBindingSpec.
func (in *FederatedWorkspaceRoleBindingSpec) DeepCopy() *FederatedWorkspaceRoleBindingSpec {
if in == nil {
return nil
}
out := new(FederatedWorkspaceRoleBindingSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceRoleBindingTemplate) DeepCopyInto(out *WorkspaceRoleBindingTemplate) {
*out = *in
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.Subjects != nil {
in, out := &in.Subjects, &out.Subjects
*out = make([]v1.Subject, len(*in))
copy(*out, *in)
}
out.RoleRef = in.RoleRef
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBindingTemplate.
func (in *WorkspaceRoleBindingTemplate) DeepCopy() *WorkspaceRoleBindingTemplate {
if in == nil {
return nil
}
out := new(WorkspaceRoleBindingTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederatedWorkspaceRoleList) DeepCopyInto(out *FederatedWorkspaceRoleList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]FederatedWorkspaceRole, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleList.
func (in *FederatedWorkspaceRoleList) DeepCopy() *FederatedWorkspaceRoleList {
if in == nil {
return nil
}
out := new(FederatedWorkspaceRoleList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedWorkspaceRoleList) 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 *FederatedWorkspaceRoleSpec) DeepCopyInto(out *FederatedWorkspaceRoleSpec) {
*out = *in
in.Template.DeepCopyInto(&out.Template)
in.Placement.DeepCopyInto(&out.Placement)
if in.Overrides != nil {
in, out := &in.Overrides, &out.Overrides
*out = make([]GenericOverrideItem, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedWorkspaceRoleSpec.
func (in *FederatedWorkspaceRoleSpec) DeepCopy() *FederatedWorkspaceRoleSpec {
if in == nil {
return nil
}
out := new(FederatedWorkspaceRoleSpec)
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
......@@ -2045,6 +2248,7 @@ func (in *GroupBindingTemplate) DeepCopy() *GroupBindingTemplate {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GroupTemplate) DeepCopyInto(out *GroupTemplate) {
*out = *in
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
return
}
......@@ -2256,6 +2460,30 @@ func (in *UserTemplate) DeepCopy() *UserTemplate {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceRoleTemplate) DeepCopyInto(out *WorkspaceRoleTemplate) {
*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])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleTemplate.
func (in *WorkspaceRoleTemplate) DeepCopy() *WorkspaceRoleTemplate {
if in == nil {
return nil
}
out := new(WorkspaceRoleTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceTemplate) DeepCopyInto(out *WorkspaceTemplate) {
*out = *in
......
/*
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"
v1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
)
// FakeFederatedUsers implements FederatedUserInterface
type FakeFederatedUsers struct {
Fake *FakeTypesV1beta1
ns string
}
var federatedusersResource = schema.GroupVersionResource{Group: "types.kubefed.io", Version: "v1beta1", Resource: "federatedusers"}
var federatedusersKind = schema.GroupVersionKind{Group: "types.kubefed.io", Version: "v1beta1", Kind: "FederatedUser"}
// Get takes name of the federatedUser, and returns the corresponding federatedUser object, and an error if there is any.
func (c *FakeFederatedUsers) Get(name string, options v1.GetOptions) (result *v1beta1.FederatedUser, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(federatedusersResource, c.ns, name), &v1beta1.FederatedUser{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedUser), err
}
// List takes label and field selectors, and returns the list of FederatedUsers that match those selectors.
func (c *FakeFederatedUsers) List(opts v1.ListOptions) (result *v1beta1.FederatedUserList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(federatedusersResource, federatedusersKind, c.ns, opts), &v1beta1.FederatedUserList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1beta1.FederatedUserList{ListMeta: obj.(*v1beta1.FederatedUserList).ListMeta}
for _, item := range obj.(*v1beta1.FederatedUserList).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 federatedUsers.
func (c *FakeFederatedUsers) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(federatedusersResource, c.ns, opts))
}
// Create takes the representation of a federatedUser and creates it. Returns the server's representation of the federatedUser, and an error, if there is any.
func (c *FakeFederatedUsers) Create(federatedUser *v1beta1.FederatedUser) (result *v1beta1.FederatedUser, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(federatedusersResource, c.ns, federatedUser), &v1beta1.FederatedUser{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedUser), err
}
// Update takes the representation of a federatedUser and updates it. Returns the server's representation of the federatedUser, and an error, if there is any.
func (c *FakeFederatedUsers) Update(federatedUser *v1beta1.FederatedUser) (result *v1beta1.FederatedUser, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(federatedusersResource, c.ns, federatedUser), &v1beta1.FederatedUser{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedUser), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeFederatedUsers) UpdateStatus(federatedUser *v1beta1.FederatedUser) (*v1beta1.FederatedUser, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(federatedusersResource, "status", c.ns, federatedUser), &v1beta1.FederatedUser{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedUser), err
}
// Delete takes name of the federatedUser and deletes it. Returns an error if one occurs.
func (c *FakeFederatedUsers) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(federatedusersResource, c.ns, name), &v1beta1.FederatedUser{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeFederatedUsers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(federatedusersResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1beta1.FederatedUserList{})
return err
}
// Patch applies the patch and returns the patched federatedUser.
func (c *FakeFederatedUsers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.FederatedUser, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(federatedusersResource, c.ns, name, pt, data, subresources...), &v1beta1.FederatedUser{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedUser), err
}
/*
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"
v1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
)
// FakeFederatedWorkspaces implements FederatedWorkspaceInterface
type FakeFederatedWorkspaces struct {
Fake *FakeTypesV1beta1
}
var federatedworkspacesResource = schema.GroupVersionResource{Group: "types.kubefed.io", Version: "v1beta1", Resource: "federatedworkspaces"}
var federatedworkspacesKind = schema.GroupVersionKind{Group: "types.kubefed.io", Version: "v1beta1", Kind: "FederatedWorkspace"}
// Get takes name of the federatedWorkspace, and returns the corresponding federatedWorkspace object, and an error if there is any.
func (c *FakeFederatedWorkspaces) Get(name string, options v1.GetOptions) (result *v1beta1.FederatedWorkspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootGetAction(federatedworkspacesResource, name), &v1beta1.FederatedWorkspace{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedWorkspace), err
}
// List takes label and field selectors, and returns the list of FederatedWorkspaces that match those selectors.
func (c *FakeFederatedWorkspaces) List(opts v1.ListOptions) (result *v1beta1.FederatedWorkspaceList, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootListAction(federatedworkspacesResource, federatedworkspacesKind, opts), &v1beta1.FederatedWorkspaceList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1beta1.FederatedWorkspaceList{ListMeta: obj.(*v1beta1.FederatedWorkspaceList).ListMeta}
for _, item := range obj.(*v1beta1.FederatedWorkspaceList).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 federatedWorkspaces.
func (c *FakeFederatedWorkspaces) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewRootWatchAction(federatedworkspacesResource, opts))
}
// Create takes the representation of a federatedWorkspace and creates it. Returns the server's representation of the federatedWorkspace, and an error, if there is any.
func (c *FakeFederatedWorkspaces) Create(federatedWorkspace *v1beta1.FederatedWorkspace) (result *v1beta1.FederatedWorkspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootCreateAction(federatedworkspacesResource, federatedWorkspace), &v1beta1.FederatedWorkspace{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedWorkspace), err
}
// Update takes the representation of a federatedWorkspace and updates it. Returns the server's representation of the federatedWorkspace, and an error, if there is any.
func (c *FakeFederatedWorkspaces) Update(federatedWorkspace *v1beta1.FederatedWorkspace) (result *v1beta1.FederatedWorkspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateAction(federatedworkspacesResource, federatedWorkspace), &v1beta1.FederatedWorkspace{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedWorkspace), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeFederatedWorkspaces) UpdateStatus(federatedWorkspace *v1beta1.FederatedWorkspace) (*v1beta1.FederatedWorkspace, error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceAction(federatedworkspacesResource, "status", federatedWorkspace), &v1beta1.FederatedWorkspace{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedWorkspace), err
}
// Delete takes name of the federatedWorkspace and deletes it. Returns an error if one occurs.
func (c *FakeFederatedWorkspaces) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewRootDeleteAction(federatedworkspacesResource, name), &v1beta1.FederatedWorkspace{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeFederatedWorkspaces) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewRootDeleteCollectionAction(federatedworkspacesResource, listOptions)
_, err := c.Fake.Invokes(action, &v1beta1.FederatedWorkspaceList{})
return err
}
// Patch applies the patch and returns the patched federatedWorkspace.
func (c *FakeFederatedWorkspaces) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.FederatedWorkspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootPatchSubresourceAction(federatedworkspacesResource, name, pt, data, subresources...), &v1beta1.FederatedWorkspace{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.FederatedWorkspace), err
}
......@@ -92,14 +92,6 @@ func (c *FakeTypesV1beta1) FederatedStatefulSets(namespace string) v1beta1.Feder
return &FakeFederatedStatefulSets{c, namespace}
}
func (c *FakeTypesV1beta1) FederatedUsers(namespace string) v1beta1.FederatedUserInterface {
return &FakeFederatedUsers{c, namespace}
}
func (c *FakeTypesV1beta1) FederatedWorkspaces() v1beta1.FederatedWorkspaceInterface {
return &FakeFederatedWorkspaces{c}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeTypesV1beta1) RESTClient() rest.Interface {
......
/*
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 v1beta1
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"
v1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
)
// FederatedUsersGetter has a method to return a FederatedUserInterface.
// A group's client should implement this interface.
type FederatedUsersGetter interface {
FederatedUsers(namespace string) FederatedUserInterface
}
// FederatedUserInterface has methods to work with FederatedUser resources.
type FederatedUserInterface interface {
Create(*v1beta1.FederatedUser) (*v1beta1.FederatedUser, error)
Update(*v1beta1.FederatedUser) (*v1beta1.FederatedUser, error)
UpdateStatus(*v1beta1.FederatedUser) (*v1beta1.FederatedUser, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1beta1.FederatedUser, error)
List(opts v1.ListOptions) (*v1beta1.FederatedUserList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.FederatedUser, err error)
FederatedUserExpansion
}
// federatedUsers implements FederatedUserInterface
type federatedUsers struct {
client rest.Interface
ns string
}
// newFederatedUsers returns a FederatedUsers
func newFederatedUsers(c *TypesV1beta1Client, namespace string) *federatedUsers {
return &federatedUsers{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the federatedUser, and returns the corresponding federatedUser object, and an error if there is any.
func (c *federatedUsers) Get(name string, options v1.GetOptions) (result *v1beta1.FederatedUser, err error) {
result = &v1beta1.FederatedUser{}
err = c.client.Get().
Namespace(c.ns).
Resource("federatedusers").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of FederatedUsers that match those selectors.
func (c *federatedUsers) List(opts v1.ListOptions) (result *v1beta1.FederatedUserList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1beta1.FederatedUserList{}
err = c.client.Get().
Namespace(c.ns).
Resource("federatedusers").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested federatedUsers.
func (c *federatedUsers) 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().
Namespace(c.ns).
Resource("federatedusers").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a federatedUser and creates it. Returns the server's representation of the federatedUser, and an error, if there is any.
func (c *federatedUsers) Create(federatedUser *v1beta1.FederatedUser) (result *v1beta1.FederatedUser, err error) {
result = &v1beta1.FederatedUser{}
err = c.client.Post().
Namespace(c.ns).
Resource("federatedusers").
Body(federatedUser).
Do().
Into(result)
return
}
// Update takes the representation of a federatedUser and updates it. Returns the server's representation of the federatedUser, and an error, if there is any.
func (c *federatedUsers) Update(federatedUser *v1beta1.FederatedUser) (result *v1beta1.FederatedUser, err error) {
result = &v1beta1.FederatedUser{}
err = c.client.Put().
Namespace(c.ns).
Resource("federatedusers").
Name(federatedUser.Name).
Body(federatedUser).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *federatedUsers) UpdateStatus(federatedUser *v1beta1.FederatedUser) (result *v1beta1.FederatedUser, err error) {
result = &v1beta1.FederatedUser{}
err = c.client.Put().
Namespace(c.ns).
Resource("federatedusers").
Name(federatedUser.Name).
SubResource("status").
Body(federatedUser).
Do().
Into(result)
return
}
// Delete takes name of the federatedUser and deletes it. Returns an error if one occurs.
func (c *federatedUsers) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("federatedusers").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *federatedUsers) 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().
Namespace(c.ns).
Resource("federatedusers").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched federatedUser.
func (c *federatedUsers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.FederatedUser, err error) {
result = &v1beta1.FederatedUser{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("federatedusers").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}
/*
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 v1beta1
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"
v1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
)
// FederatedWorkspacesGetter has a method to return a FederatedWorkspaceInterface.
// A group's client should implement this interface.
type FederatedWorkspacesGetter interface {
FederatedWorkspaces() FederatedWorkspaceInterface
}
// FederatedWorkspaceInterface has methods to work with FederatedWorkspace resources.
type FederatedWorkspaceInterface interface {
Create(*v1beta1.FederatedWorkspace) (*v1beta1.FederatedWorkspace, error)
Update(*v1beta1.FederatedWorkspace) (*v1beta1.FederatedWorkspace, error)
UpdateStatus(*v1beta1.FederatedWorkspace) (*v1beta1.FederatedWorkspace, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1beta1.FederatedWorkspace, error)
List(opts v1.ListOptions) (*v1beta1.FederatedWorkspaceList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.FederatedWorkspace, err error)
FederatedWorkspaceExpansion
}
// federatedWorkspaces implements FederatedWorkspaceInterface
type federatedWorkspaces struct {
client rest.Interface
}
// newFederatedWorkspaces returns a FederatedWorkspaces
func newFederatedWorkspaces(c *TypesV1beta1Client) *federatedWorkspaces {
return &federatedWorkspaces{
client: c.RESTClient(),
}
}
// Get takes name of the federatedWorkspace, and returns the corresponding federatedWorkspace object, and an error if there is any.
func (c *federatedWorkspaces) Get(name string, options v1.GetOptions) (result *v1beta1.FederatedWorkspace, err error) {
result = &v1beta1.FederatedWorkspace{}
err = c.client.Get().
Resource("federatedworkspaces").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of FederatedWorkspaces that match those selectors.
func (c *federatedWorkspaces) List(opts v1.ListOptions) (result *v1beta1.FederatedWorkspaceList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1beta1.FederatedWorkspaceList{}
err = c.client.Get().
Resource("federatedworkspaces").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested federatedWorkspaces.
func (c *federatedWorkspaces) 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("federatedworkspaces").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a federatedWorkspace and creates it. Returns the server's representation of the federatedWorkspace, and an error, if there is any.
func (c *federatedWorkspaces) Create(federatedWorkspace *v1beta1.FederatedWorkspace) (result *v1beta1.FederatedWorkspace, err error) {
result = &v1beta1.FederatedWorkspace{}
err = c.client.Post().
Resource("federatedworkspaces").
Body(federatedWorkspace).
Do().
Into(result)
return
}
// Update takes the representation of a federatedWorkspace and updates it. Returns the server's representation of the federatedWorkspace, and an error, if there is any.
func (c *federatedWorkspaces) Update(federatedWorkspace *v1beta1.FederatedWorkspace) (result *v1beta1.FederatedWorkspace, err error) {
result = &v1beta1.FederatedWorkspace{}
err = c.client.Put().
Resource("federatedworkspaces").
Name(federatedWorkspace.Name).
Body(federatedWorkspace).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *federatedWorkspaces) UpdateStatus(federatedWorkspace *v1beta1.FederatedWorkspace) (result *v1beta1.FederatedWorkspace, err error) {
result = &v1beta1.FederatedWorkspace{}
err = c.client.Put().
Resource("federatedworkspaces").
Name(federatedWorkspace.Name).
SubResource("status").
Body(federatedWorkspace).
Do().
Into(result)
return
}
// Delete takes name of the federatedWorkspace and deletes it. Returns an error if one occurs.
func (c *federatedWorkspaces) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Resource("federatedworkspaces").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *federatedWorkspaces) 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("federatedworkspaces").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched federatedWorkspace.
func (c *federatedWorkspaces) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.FederatedWorkspace, err error) {
result = &v1beta1.FederatedWorkspace{}
err = c.client.Patch(pt).
Resource("federatedworkspaces").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}
......@@ -49,7 +49,3 @@ type FederatedSecretExpansion interface{}
type FederatedServiceExpansion interface{}
type FederatedStatefulSetExpansion interface{}
type FederatedUserExpansion interface{}
type FederatedWorkspaceExpansion interface{}
......@@ -42,8 +42,6 @@ type TypesV1beta1Interface interface {
FederatedSecretsGetter
FederatedServicesGetter
FederatedStatefulSetsGetter
FederatedUsersGetter
FederatedWorkspacesGetter
}
// TypesV1beta1Client is used to interact with features provided by the types.kubefed.io group.
......@@ -115,14 +113,6 @@ func (c *TypesV1beta1Client) FederatedStatefulSets(namespace string) FederatedSt
return newFederatedStatefulSets(c, namespace)
}
func (c *TypesV1beta1Client) FederatedUsers(namespace string) FederatedUserInterface {
return newFederatedUsers(c, namespace)
}
func (c *TypesV1beta1Client) FederatedWorkspaces() FederatedWorkspaceInterface {
return newFederatedWorkspaces(c)
}
// NewForConfig creates a new TypesV1beta1Client for the given config.
func NewForConfig(c *rest.Config) (*TypesV1beta1Client, error) {
config := *c
......
......@@ -171,10 +171,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Types().V1beta1().FederatedServices().Informer()}, nil
case v1beta1.SchemeGroupVersion.WithResource("federatedstatefulsets"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Types().V1beta1().FederatedStatefulSets().Informer()}, nil
case v1beta1.SchemeGroupVersion.WithResource("federatedusers"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Types().V1beta1().FederatedUsers().Informer()}, nil
case v1beta1.SchemeGroupVersion.WithResource("federatedworkspaces"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Types().V1beta1().FederatedWorkspaces().Informer()}, nil
}
......
/*
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 v1beta1
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"
typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
v1beta1 "kubesphere.io/kubesphere/pkg/client/listers/types/v1beta1"
)
// FederatedUserInformer provides access to a shared informer and lister for
// FederatedUsers.
type FederatedUserInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1beta1.FederatedUserLister
}
type federatedUserInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewFederatedUserInformer constructs a new informer for FederatedUser 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 NewFederatedUserInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredFederatedUserInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredFederatedUserInformer constructs a new informer for FederatedUser 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 NewFilteredFederatedUserInformer(client versioned.Interface, namespace string, 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.TypesV1beta1().FederatedUsers(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.TypesV1beta1().FederatedUsers(namespace).Watch(options)
},
},
&typesv1beta1.FederatedUser{},
resyncPeriod,
indexers,
)
}
func (f *federatedUserInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredFederatedUserInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *federatedUserInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&typesv1beta1.FederatedUser{}, f.defaultInformer)
}
func (f *federatedUserInformer) Lister() v1beta1.FederatedUserLister {
return v1beta1.NewFederatedUserLister(f.Informer().GetIndexer())
}
/*
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 v1beta1
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"
typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
v1beta1 "kubesphere.io/kubesphere/pkg/client/listers/types/v1beta1"
)
// FederatedWorkspaceInformer provides access to a shared informer and lister for
// FederatedWorkspaces.
type FederatedWorkspaceInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1beta1.FederatedWorkspaceLister
}
type federatedWorkspaceInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// NewFederatedWorkspaceInformer constructs a new informer for FederatedWorkspace 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 NewFederatedWorkspaceInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredFederatedWorkspaceInformer(client, resyncPeriod, indexers, nil)
}
// NewFilteredFederatedWorkspaceInformer constructs a new informer for FederatedWorkspace 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 NewFilteredFederatedWorkspaceInformer(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.TypesV1beta1().FederatedWorkspaces().List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.TypesV1beta1().FederatedWorkspaces().Watch(options)
},
},
&typesv1beta1.FederatedWorkspace{},
resyncPeriod,
indexers,
)
}
func (f *federatedWorkspaceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredFederatedWorkspaceInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *federatedWorkspaceInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&typesv1beta1.FederatedWorkspace{}, f.defaultInformer)
}
func (f *federatedWorkspaceInformer) Lister() v1beta1.FederatedWorkspaceLister {
return v1beta1.NewFederatedWorkspaceLister(f.Informer().GetIndexer())
}
......@@ -56,10 +56,6 @@ type Interface interface {
FederatedServices() FederatedServiceInformer
// FederatedStatefulSets returns a FederatedStatefulSetInformer.
FederatedStatefulSets() FederatedStatefulSetInformer
// FederatedUsers returns a FederatedUserInformer.
FederatedUsers() FederatedUserInformer
// FederatedWorkspaces returns a FederatedWorkspaceInformer.
FederatedWorkspaces() FederatedWorkspaceInformer
}
type version struct {
......@@ -152,13 +148,3 @@ func (v *version) FederatedServices() FederatedServiceInformer {
func (v *version) FederatedStatefulSets() FederatedStatefulSetInformer {
return &federatedStatefulSetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// FederatedUsers returns a FederatedUserInformer.
func (v *version) FederatedUsers() FederatedUserInformer {
return &federatedUserInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// FederatedWorkspaces returns a FederatedWorkspaceInformer.
func (v *version) FederatedWorkspaces() FederatedWorkspaceInformer {
return &federatedWorkspaceInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
}
......@@ -137,15 +137,3 @@ type FederatedStatefulSetListerExpansion interface{}
// FederatedStatefulSetNamespaceListerExpansion allows custom methods to be added to
// FederatedStatefulSetNamespaceLister.
type FederatedStatefulSetNamespaceListerExpansion interface{}
// FederatedUserListerExpansion allows custom methods to be added to
// FederatedUserLister.
type FederatedUserListerExpansion interface{}
// FederatedUserNamespaceListerExpansion allows custom methods to be added to
// FederatedUserNamespaceLister.
type FederatedUserNamespaceListerExpansion interface{}
// FederatedWorkspaceListerExpansion allows custom methods to be added to
// FederatedWorkspaceLister.
type FederatedWorkspaceListerExpansion interface{}
/*
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 v1beta1
import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
v1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
)
// FederatedUserLister helps list FederatedUsers.
type FederatedUserLister interface {
// List lists all FederatedUsers in the indexer.
List(selector labels.Selector) (ret []*v1beta1.FederatedUser, err error)
// FederatedUsers returns an object that can list and get FederatedUsers.
FederatedUsers(namespace string) FederatedUserNamespaceLister
FederatedUserListerExpansion
}
// federatedUserLister implements the FederatedUserLister interface.
type federatedUserLister struct {
indexer cache.Indexer
}
// NewFederatedUserLister returns a new FederatedUserLister.
func NewFederatedUserLister(indexer cache.Indexer) FederatedUserLister {
return &federatedUserLister{indexer: indexer}
}
// List lists all FederatedUsers in the indexer.
func (s *federatedUserLister) List(selector labels.Selector) (ret []*v1beta1.FederatedUser, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.FederatedUser))
})
return ret, err
}
// FederatedUsers returns an object that can list and get FederatedUsers.
func (s *federatedUserLister) FederatedUsers(namespace string) FederatedUserNamespaceLister {
return federatedUserNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// FederatedUserNamespaceLister helps list and get FederatedUsers.
type FederatedUserNamespaceLister interface {
// List lists all FederatedUsers in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1beta1.FederatedUser, err error)
// Get retrieves the FederatedUser from the indexer for a given namespace and name.
Get(name string) (*v1beta1.FederatedUser, error)
FederatedUserNamespaceListerExpansion
}
// federatedUserNamespaceLister implements the FederatedUserNamespaceLister
// interface.
type federatedUserNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all FederatedUsers in the indexer for a given namespace.
func (s federatedUserNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.FederatedUser, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.FederatedUser))
})
return ret, err
}
// Get retrieves the FederatedUser from the indexer for a given namespace and name.
func (s federatedUserNamespaceLister) Get(name string) (*v1beta1.FederatedUser, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta1.Resource("federateduser"), name)
}
return obj.(*v1beta1.FederatedUser), nil
}
/*
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 v1beta1
import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
v1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
)
// FederatedWorkspaceLister helps list FederatedWorkspaces.
type FederatedWorkspaceLister interface {
// List lists all FederatedWorkspaces in the indexer.
List(selector labels.Selector) (ret []*v1beta1.FederatedWorkspace, err error)
// Get retrieves the FederatedWorkspace from the index for a given name.
Get(name string) (*v1beta1.FederatedWorkspace, error)
FederatedWorkspaceListerExpansion
}
// federatedWorkspaceLister implements the FederatedWorkspaceLister interface.
type federatedWorkspaceLister struct {
indexer cache.Indexer
}
// NewFederatedWorkspaceLister returns a new FederatedWorkspaceLister.
func NewFederatedWorkspaceLister(indexer cache.Indexer) FederatedWorkspaceLister {
return &federatedWorkspaceLister{indexer: indexer}
}
// List lists all FederatedWorkspaces in the indexer.
func (s *federatedWorkspaceLister) List(selector labels.Selector) (ret []*v1beta1.FederatedWorkspace, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.FederatedWorkspace))
})
return ret, err
}
// Get retrieves the FederatedWorkspace from the index for a given name.
func (s *federatedWorkspaceLister) Get(name string) (*v1beta1.FederatedWorkspace, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta1.Resource("federatedworkspace"), name)
}
return obj.(*v1beta1.FederatedWorkspace), nil
}
......@@ -49,21 +49,20 @@ import (
const (
successSynced = "Synced"
messageResourceSynced = "Group synced successfully"
controllerName = "groupbinding-controller"
controllerName = "group-controller"
finalizer = "finalizers.kubesphere.io/groups"
)
type Controller struct {
controller.BaseController
scheme *runtime.Scheme
k8sClient kubernetes.Interface
ksClient kubesphere.Interface
groupInformer iamv1alpha2informers.GroupInformer
groupLister iamv1alpha1listers.GroupLister
recorder record.EventRecorder
federatedGroupInformer fedv1beta1informers.FederatedGroupInformer
federatedGroupLister fedv1beta1lister.FederatedGroupLister
multiClusterEnabled bool
scheme *runtime.Scheme
k8sClient kubernetes.Interface
ksClient kubesphere.Interface
groupInformer iamv1alpha2informers.GroupInformer
groupLister iamv1alpha1listers.GroupLister
recorder record.EventRecorder
federatedGroupLister fedv1beta1lister.FederatedGroupLister
multiClusterEnabled bool
}
// NewController creates Group Controller instance
......@@ -81,18 +80,17 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
Synced: []cache.InformerSynced{groupInformer.Informer().HasSynced},
Name: controllerName,
},
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName}),
k8sClient: k8sClient,
ksClient: ksClient,
groupInformer: groupInformer,
groupLister: groupInformer.Lister(),
federatedGroupInformer: federatedGroupInformer,
federatedGroupLister: federatedGroupInformer.Lister(),
multiClusterEnabled: multiClusterEnabled,
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName}),
k8sClient: k8sClient,
ksClient: ksClient,
groupInformer: groupInformer,
groupLister: groupInformer.Lister(),
multiClusterEnabled: multiClusterEnabled,
}
if ctl.multiClusterEnabled {
ctl.Synced = append(ctl.Synced, ctl.federatedGroupInformer.Informer().HasSynced)
ctl.federatedGroupLister = federatedGroupInformer.Lister()
ctl.Synced = append(ctl.Synced, federatedGroupInformer.Informer().HasSynced)
}
ctl.Handler = ctl.reconcile
......
......@@ -56,15 +56,13 @@ const (
type Controller struct {
controller.BaseController
scheme *runtime.Scheme
k8sClient kubernetes.Interface
ksClient kubesphere.Interface
groupBindingInformer iamv1alpha2informers.GroupBindingInformer
groupBindingLister iamv1alpha2listers.GroupBindingLister
recorder record.EventRecorder
federatedGroupBindingInformer fedv1beta1informers.FederatedGroupBindingInformer
federatedGroupBindingLister fedv1beta1lister.FederatedGroupBindingLister
multiClusterEnabled bool
scheme *runtime.Scheme
k8sClient kubernetes.Interface
ksClient kubesphere.Interface
groupBindingLister iamv1alpha2listers.GroupBindingLister
recorder record.EventRecorder
federatedGroupBindingLister fedv1beta1lister.FederatedGroupBindingLister
multiClusterEnabled bool
}
// NewController creates GroupBinding Controller instance
......@@ -82,18 +80,16 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
Synced: []cache.InformerSynced{groupBindingInformer.Informer().HasSynced},
Name: controllerName,
},
k8sClient: k8sClient,
ksClient: ksClient,
groupBindingInformer: groupBindingInformer,
groupBindingLister: groupBindingInformer.Lister(),
federatedGroupBindingInformer: federatedGroupBindingInformer,
federatedGroupBindingLister: federatedGroupBindingInformer.Lister(),
multiClusterEnabled: multiClusterEnabled,
recorder: recorder,
k8sClient: k8sClient,
ksClient: ksClient,
groupBindingLister: groupBindingInformer.Lister(),
multiClusterEnabled: multiClusterEnabled,
recorder: recorder,
}
ctl.Handler = ctl.reconcile
if ctl.multiClusterEnabled {
ctl.Synced = append(ctl.Synced, ctl.federatedGroupBindingInformer.Informer().HasSynced)
ctl.federatedGroupBindingLister = federatedGroupBindingInformer.Lister()
ctl.Synced = append(ctl.Synced, federatedGroupBindingInformer.Informer().HasSynced)
}
klog.Info("Setting up event handlers")
groupBindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
......
......@@ -20,240 +20,207 @@ import (
"bytes"
"context"
"fmt"
"github.com/go-logr/logr"
appsv1 "k8s.io/api/apps/v1"
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/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/klog"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
controllerutils "kubesphere.io/kubesphere/pkg/controller/utils/controller"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"reflect"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)
// 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.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
const (
controllerName = "namespace-controller"
)
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileNamespace{
Client: mgr.GetClient(),
scheme: mgr.GetScheme(),
}
// Reconciler reconciles a Namespace object
type Reconciler struct {
client.Client
Logger logr.Logger
Recorder record.EventRecorder
MaxConcurrentReconciles int
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("namespace-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
if r.Client == nil {
r.Client = mgr.GetClient()
}
// Watch for changes to Namespace
err = c.Watch(&source.Kind{Type: &corev1.Namespace{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
if r.Logger == nil {
r.Logger = ctrl.Log.WithName("controllers").WithName(controllerName)
}
return nil
}
var _ reconcile.Reconciler = &ReconcileNamespace{}
// ReconcileNamespace reconciles a Namespace object
type ReconcileNamespace struct {
client.Client
scheme *runtime.Scheme
if r.Recorder == nil {
r.Recorder = mgr.GetEventRecorderFor(controllerName)
}
if r.MaxConcurrentReconciles <= 0 {
r.MaxConcurrentReconciles = 1
}
return ctrl.NewControllerManagedBy(mgr).
Named(controllerName).
WithOptions(controller.Options{
MaxConcurrentReconciles: r.MaxConcurrentReconciles,
}).
For(&corev1.Namespace{}).
Complete(r)
}
// Reconcile reads that state of the cluster for a Namespace object and makes changes based on the state read
// and what is in the Namespace.Spec
// +kubebuilder:rbac:groups=core.kubesphere.io,resources=namespaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core.kubesphere.io,resources=namespaces/status,verbs=get;update;patch
func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Namespace instance
instance := &corev1.Namespace{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
// The object is being deleted
// our finalizer is present, so lets handle our external dependency
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch
// +kubebuilder:rbac:groups=iam.kubesphere.io,resources=rolebases,verbs=get;list;watch
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=get;list;watch
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
logger := r.Logger.WithValues("namespace", req.NamespacedName)
rootCtx := context.Background()
namespace := &corev1.Namespace{}
if err := r.Get(rootCtx, req.NamespacedName, namespace); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// name of your custom finalizer
finalizer := "finalizers.kubesphere.io/namespaces"
if instance.ObjectMeta.DeletionTimestamp.IsZero() {
if namespace.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object.
if !sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, finalizer)
if instance.Labels == nil {
instance.Labels = make(map[string]string)
if !sliceutil.HasString(namespace.ObjectMeta.Finalizers, finalizer) {
// create only once, ignore already exists error
if err := r.initCreatorRoleBinding(rootCtx, logger, namespace); err != nil {
return ctrl.Result{}, err
}
namespace.ObjectMeta.Finalizers = append(namespace.ObjectMeta.Finalizers, finalizer)
if namespace.Labels == nil {
namespace.Labels = make(map[string]string)
}
instance.Labels[constants.NamespaceLabelKey] = instance.Name
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
// used for NetworkPolicyPeer.NamespaceSelector
namespace.Labels[constants.NamespaceLabelKey] = namespace.Name
if err := r.Update(rootCtx, namespace); err != nil {
return ctrl.Result{}, err
}
}
} else {
// The object is being deleted
if sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
if err = r.deleteRouter(instance.Name); err != nil {
return reconcile.Result{}, err
if sliceutil.HasString(namespace.ObjectMeta.Finalizers, finalizer) {
if err := r.deleteRouter(rootCtx, logger, namespace.Name); err != nil {
return ctrl.Result{}, err
}
// remove our finalizer from the list and update it.
instance.ObjectMeta.Finalizers = sliceutil.RemoveString(instance.ObjectMeta.Finalizers, func(item string) bool {
namespace.ObjectMeta.Finalizers = sliceutil.RemoveString(namespace.ObjectMeta.Finalizers, func(item string) bool {
return item == finalizer
})
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
if err := r.Update(rootCtx, namespace); err != nil {
return ctrl.Result{}, err
}
}
// Our finalizer has finished, so the reconciler can do nothing.
return reconcile.Result{}, nil
return ctrl.Result{}, nil
}
// initialize subresource if created by kubesphere
if workspace := instance.Labels[constants.WorkspaceLabelKey]; workspace != "" {
if err = r.bindWorkspace(instance); err != nil {
return reconcile.Result{}, err
}
if err = r.initRoles(instance); err != nil {
return reconcile.Result{}, err
if workspace := namespace.Labels[tenantv1alpha1.WorkspaceLabel]; workspace != "" {
if err := r.bindWorkspace(rootCtx, logger, namespace); err != nil {
return ctrl.Result{}, err
}
if err = r.initCreatorRoleBinding(instance); err != nil {
return reconcile.Result{}, err
if err := r.initRoles(rootCtx, logger, namespace); err != nil {
return ctrl.Result{}, err
}
} else {
r.unbindWorkspace(instance)
if err := r.unbindWorkspace(rootCtx, logger, namespace); err != nil {
return ctrl.Result{}, err
}
}
return reconcile.Result{}, nil
r.Recorder.Event(namespace, corev1.EventTypeNormal, controllerutils.SuccessSynced, controllerutils.MessageResourceSynced)
return ctrl.Result{}, nil
}
func (r *ReconcileNamespace) bindWorkspace(namespace *corev1.Namespace) error {
workspaceName := namespace.Labels[constants.WorkspaceLabelKey]
func (r *Reconciler) bindWorkspace(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
workspace := &tenantv1alpha1.Workspace{}
if err := r.Get(context.TODO(), types.NamespacedName{Name: workspaceName}, workspace); err != nil {
// skip if workspace not found
if errors.IsNotFound(err) {
klog.Warning(err)
return nil
if err := r.Get(ctx, types.NamespacedName{Name: namespace.Labels[constants.WorkspaceLabelKey]}, workspace); err != nil {
// remove existed owner reference if workspace not found
if errors.IsNotFound(err) && k8sutil.IsControlledBy(namespace.OwnerReferences, tenantv1alpha1.ResourceKindWorkspace, "") {
return r.unbindWorkspace(ctx, logger, namespace)
}
klog.Error(err)
return err
// skip if workspace not found
return client.IgnoreNotFound(err)
}
// owner reference not match workspace label
if !metav1.IsControlledBy(namespace, workspace) {
workspace.OwnerReferences = removeWorkspaceOwnerReference(workspace.OwnerReferences)
if err := controllerutil.SetControllerReference(workspace, namespace, r.scheme); err != nil {
klog.Error(err)
namespace := namespace.DeepCopy()
namespace.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(namespace.OwnerReferences)
if err := controllerutil.SetControllerReference(workspace, namespace, scheme.Scheme); err != nil {
logger.Error(err, "set controller reference failed")
return err
}
if err := r.Update(context.TODO(), namespace); err != nil {
klog.Error(err)
logger.V(4).Info("update namespace owner reference", "workspace", workspace.Name)
if err := r.Update(ctx, namespace); err != nil {
logger.Error(err, "update namespace failed")
return err
}
}
return nil
}
func (r *ReconcileNamespace) unbindWorkspace(namespace *corev1.Namespace) error {
func (r *Reconciler) unbindWorkspace(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
if k8sutil.IsControlledBy(namespace.OwnerReferences, tenantv1alpha1.ResourceKindWorkspace, "") {
namespace := namespace.DeepCopy()
namespace.OwnerReferences = removeWorkspaceOwnerReference(namespace.OwnerReferences)
if err := r.Update(context.TODO(), namespace); err != nil {
klog.Error(err)
namespace.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(namespace.OwnerReferences)
logger.V(4).Info("remove owner reference", "workspace", namespace.Labels[constants.WorkspaceLabelKey])
if err := r.Update(ctx, namespace); err != nil {
logger.Error(err, "update owner reference failed")
return err
}
}
return nil
}
// Remove workspace kind owner reference of the namespace
func removeWorkspaceOwnerReference(ownerReferences []metav1.OwnerReference) []metav1.OwnerReference {
tmp := make([]metav1.OwnerReference, 0)
for _, owner := range ownerReferences {
if owner.Kind != tenantv1alpha1.ResourceKindWorkspace {
tmp = append(tmp, owner)
}
}
return tmp
}
func (r *ReconcileNamespace) deleteRouter(namespace string) error {
func (r *Reconciler) deleteRouter(ctx context.Context, logger logr.Logger, namespace string) error {
routerName := constants.IngressControllerPrefix + namespace
// delete service first
found := corev1.Service{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: constants.IngressControllerNamespace, Name: routerName}, &found)
service := corev1.Service{}
err := r.Get(ctx, types.NamespacedName{Namespace: constants.IngressControllerNamespace, Name: routerName}, &service)
if err != nil {
if errors.IsNotFound(err) {
return nil
}
klog.Error(err)
return client.IgnoreNotFound(err)
}
err = r.Delete(context.TODO(), &found)
logger.V(4).Info("delete router service", "namespace", service.Namespace, "service", service.Name)
err = r.Delete(ctx, &service)
if err != nil {
klog.Error(err)
return err
return client.IgnoreNotFound(err)
}
// delete deployment
deploy := appsv1.Deployment{}
err = r.Get(context.TODO(), types.NamespacedName{Namespace: constants.IngressControllerNamespace, Name: routerName}, &deploy)
err = r.Get(ctx, types.NamespacedName{Namespace: constants.IngressControllerNamespace, Name: routerName}, &deploy)
if err != nil {
if errors.IsNotFound(err) {
return nil
}
klog.Error(err)
logger.Error(err, "delete router deployment failed")
return err
}
err = r.Delete(context.TODO(), &deploy)
logger.V(4).Info("delete router deployment", "namespace", deploy.Namespace, "deployment", deploy.Name)
err = r.Delete(ctx, &deploy)
if err != nil {
klog.Error(err)
return err
return client.IgnoreNotFound(err)
}
return nil
}
func (r *ReconcileNamespace) initRoles(namespace *corev1.Namespace) error {
var roleBases iamv1alpha2.RoleBaseList
func (r *Reconciler) initRoles(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
var templates iamv1alpha2.RoleBaseList
var labelKey string
// filtering initial roles by label
if namespace.Labels[constants.DevOpsProjectLabelKey] != "" {
......@@ -263,29 +230,26 @@ func (r *ReconcileNamespace) initRoles(namespace *corev1.Namespace) error {
// scope.kubesphere.io/namespace: ""
labelKey = fmt.Sprintf(iamv1alpha2.ScopeLabelFormat, iamv1alpha2.ScopeNamespace)
}
err := r.List(context.Background(), &roleBases, client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(labels.Set{labelKey: ""})})
if err != nil {
klog.Error(err)
if err := r.List(ctx, &templates, client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(labels.Set{labelKey: ""})}); err != nil {
logger.Error(err, "list role bases failed")
return err
}
for _, roleBase := range roleBases.Items {
for _, template := range templates.Items {
var role rbacv1.Role
if err = yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(roleBase.Role.Raw), 1024).Decode(&role); err == nil && role.Kind == iamv1alpha2.ResourceKindRole {
if err := yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(template.Role.Raw), 1024).Decode(&role); err == nil && role.Kind == iamv1alpha2.ResourceKindRole {
var old rbacv1.Role
err := r.Client.Get(context.Background(), types.NamespacedName{Namespace: namespace.Name, Name: role.Name}, &old)
if err != nil {
if err := r.Client.Get(ctx, types.NamespacedName{Namespace: namespace.Name, Name: role.Name}, &old); err != nil {
if errors.IsNotFound(err) {
role.Namespace = namespace.Name
err = r.Client.Create(context.Background(), &role)
if err != nil {
klog.Error(err)
logger.V(4).Info("init builtin role", "role", role.Name)
if err := r.Client.Create(ctx, &role); err != nil {
logger.Error(err, "create role failed")
return err
}
continue
}
}
if !reflect.DeepEqual(role.Labels, old.Labels) ||
!reflect.DeepEqual(role.Annotations, old.Annotations) ||
!reflect.DeepEqual(role.Rules, old.Rules) {
......@@ -294,36 +258,46 @@ func (r *ReconcileNamespace) initRoles(namespace *corev1.Namespace) error {
old.Annotations = role.Annotations
old.Rules = role.Rules
if err := r.Update(context.Background(), &old); err != nil {
klog.Error(err)
logger.V(4).Info("update builtin role", "role", role.Name)
if err := r.Update(ctx, &old); err != nil {
logger.Error(err, "update role failed")
return err
}
}
} else if err != nil {
logger.Error(fmt.Errorf("invalid role base found"), "init roles failed", "name", template.Name)
}
}
return nil
}
func (r *ReconcileNamespace) initCreatorRoleBinding(namespace *corev1.Namespace) error {
func (r *Reconciler) initCreatorRoleBinding(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace) error {
creator := namespace.Annotations[constants.CreatorAnnotationKey]
if creator == "" {
return nil
}
var user iamv1alpha2.User
if err := r.Get(context.Background(), types.NamespacedName{Name: creator}, &user); err != nil {
if errors.IsNotFound(err) {
if err := r.Get(ctx, types.NamespacedName{Name: creator}, &user); err != nil {
return client.IgnoreNotFound(err)
}
creatorRoleBinding := newCreatorRoleBinding(creator, namespace.Name)
logger.V(4).Info("init creator role binding", "creator", user.Name)
if err := r.Client.Create(ctx, creatorRoleBinding); err != nil {
if errors.IsAlreadyExists(err) {
return nil
}
klog.Error(err)
logger.Error(err, "create role binding failed")
return err
}
return nil
}
creatorRoleBinding := &rbacv1.RoleBinding{
func newCreatorRoleBinding(creator string, namespace string) *rbacv1.RoleBinding {
return &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", creator, iamv1alpha2.NamespaceAdmin),
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: creator},
Namespace: namespace.Name,
Namespace: namespace,
},
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
......@@ -338,14 +312,4 @@ func (r *ReconcileNamespace) initCreatorRoleBinding(namespace *corev1.Namespace)
},
},
}
if err := r.Client.Create(context.Background(), creatorRoleBinding); err != nil {
if errors.IsAlreadyExists(err) {
return nil
}
klog.Error(err)
return err
}
return nil
}
......@@ -17,59 +17,84 @@ limitations under the License.
package namespace
import (
stdlog "log"
"github.com/onsi/gomega/gexec"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/klogr"
"kubesphere.io/kubesphere/pkg/apis"
"os"
"path/filepath"
"sync"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"testing"
"time"
"github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
)
var cfg *rest.Config
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var k8sClient client.Client
var k8sManager ctrl.Manager
var testEnv *envtest.Environment
func TestNamespaceController(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Namespace Controller Test Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(klogr.New())
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
By("bootstrapping test environment")
t := true
if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" {
testEnv = &envtest.Environment{
UseExistingCluster: &t,
}
} else {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
AttachControlPlaneOutput: false,
}
}
code := m.Run()
t.Stop()
os.Exit(code)
}
cfg, err := testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = apis.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
MetricsBindAddress: "0",
})
return fn, requests
}
Expect(err).ToNot(HaveOccurred())
err = (&Reconciler{}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
err = k8sManager.Start(ctrl.SetupSignalHandler())
Expect(err).ToNot(HaveOccurred())
}()
return stop, wg
}
k8sClient = k8sManager.GetClient()
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
gexec.KillAndWait(5 * time.Second)
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
......@@ -15,3 +15,88 @@ limitations under the License.
*/
package namespace
import (
"context"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"time"
)
var _ = Describe("Namespace", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
workspace := &tenantv1alpha1.Workspace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-workspace",
},
}
BeforeEach(func() {
// Create workspace
Expect(k8sClient.Create(context.Background(), workspace)).Should(Succeed())
})
// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("Namespace Controller", func() {
It("Should create successfully", func() {
namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-namespace",
Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: workspace.Name},
},
}
// Create namespace
Expect(k8sClient.Create(context.Background(), namespace)).Should(Succeed())
By("Expecting to create namespace successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: namespace.Name}, namespace)
return !namespace.CreationTimestamp.IsZero()
}, timeout, interval).Should(BeTrue())
By("Expecting to set owner reference successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: namespace.Name}, namespace)
return len(namespace.OwnerReferences) > 0
}, timeout, interval).Should(BeTrue())
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace)).Should(Succeed())
controlled := true
expectedOwnerReference := metav1.OwnerReference{
Kind: workspace.Kind,
APIVersion: workspace.APIVersion,
UID: workspace.UID,
Name: workspace.Name,
Controller: &controlled,
BlockOwnerDeletion: &controlled,
}
By("Expecting to bind workspace successfully")
Expect(namespace.OwnerReferences).To(ContainElement(expectedOwnerReference))
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace)).Should(Succeed())
By("Expecting to update namespace successfully")
updated := namespace.DeepCopy()
updated.Labels[constants.WorkspaceLabelKey] = "workspace-not-exist"
Expect(k8sClient.Update(context.Background(), updated)).Should(Succeed())
By("Expecting to unbind workspace successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: namespace.Name}, namespace)
return len(namespace.OwnerReferences) == 0
}, timeout, interval).Should(BeTrue())
})
})
})
......@@ -27,6 +27,13 @@ import (
"k8s.io/klog"
)
const (
// SuccessSynced is used as part of the Event 'reason' when a Foo is synced
SuccessSynced = "Synced"
// is synced successfully
MessageResourceSynced = "Synced successfully"
)
// BaseController provides a Controller template for watching a primary resources that defined as CRD.
type BaseController struct {
// Workers will wait informer caches to be synced
......
......@@ -18,104 +18,134 @@ package workspace
import (
"context"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
controllerutils "kubesphere.io/kubesphere/pkg/controller/utils/controller"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// Add creates a new Workspace 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.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
const (
controllerName = "workspace-controller"
)
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileWorkspace{Client: mgr.GetClient(), scheme: mgr.GetScheme(),
recorder: mgr.GetEventRecorderFor("workspace-controller")}
// Reconciler reconciles a Workspace object
type Reconciler struct {
client.Client
Logger logr.Logger
Recorder record.EventRecorder
MaxConcurrentReconciles int
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("workspace-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
if r.Client == nil {
r.Client = mgr.GetClient()
}
// Watch for changes to Workspace
err = c.Watch(&source.Kind{Type: &tenantv1alpha1.Workspace{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
if r.Logger == nil {
r.Logger = ctrl.Log.WithName("controllers").WithName(controllerName)
}
return nil
}
var _ reconcile.Reconciler = &ReconcileWorkspace{}
// ReconcileWorkspace reconciles a Workspace object
type ReconcileWorkspace struct {
client.Client
scheme *runtime.Scheme
recorder record.EventRecorder
if r.Recorder == nil {
r.Recorder = mgr.GetEventRecorderFor(controllerName)
}
if r.MaxConcurrentReconciles <= 0 {
r.MaxConcurrentReconciles = 1
}
return ctrl.NewControllerManagedBy(mgr).
Named(controllerName).
WithOptions(controller.Options{
MaxConcurrentReconciles: r.MaxConcurrentReconciles,
}).
For(&tenantv1alpha1.Workspace{}).
Complete(r)
}
// Reconcile reads that state of the cluster for a Workspace object and makes changes based on the state read
// and what is in the Workspace.Spec
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces/status,verbs=get;update;patch
func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Workspace instance
instance := &tenantv1alpha1.Workspace{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
// +kubebuilder:rbac:groups=iam.kubesphere.io,resources=users,verbs=get;list;watch
// +kubebuilder:rbac:groups=iam.kubesphere.io,resources=rolebases,verbs=get;list;watch
// +kubebuilder:rbac:groups=iam.kubesphere.io,resources=workspaceroles,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=iam.kubesphere.io,resources=workspacerolebindings,verbs=get;list;watch;create;update;patch;delete
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
logger := r.Logger.WithValues("workspace", req.NamespacedName)
rootCtx := context.Background()
workspace := &tenantv1alpha1.Workspace{}
if err := r.Get(rootCtx, req.NamespacedName, workspace); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// controlled kubefed-controller-manager
if workspace.Labels[constants.KubefedManagedLabel] == "true" {
return ctrl.Result{}, nil
}
// name of your custom finalizer
finalizer := "finalizers.tenant.kubesphere.io"
if instance.ObjectMeta.DeletionTimestamp.IsZero() {
if workspace.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object.
if !sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, finalizer)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
if !sliceutil.HasString(workspace.ObjectMeta.Finalizers, finalizer) {
workspace.ObjectMeta.Finalizers = append(workspace.ObjectMeta.Finalizers, finalizer)
if err := r.Update(rootCtx, workspace); err != nil {
return ctrl.Result{}, err
}
}
} else {
// The object is being deleted
if sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
// our finalizer is present, so lets handle our external dependency
if sliceutil.HasString(workspace.ObjectMeta.Finalizers, finalizer) {
// remove our finalizer from the list and update it.
instance.ObjectMeta.Finalizers = sliceutil.RemoveString(instance.ObjectMeta.Finalizers, func(item string) bool {
workspace.ObjectMeta.Finalizers = sliceutil.RemoveString(workspace.ObjectMeta.Finalizers, func(item string) bool {
return item == finalizer
})
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
logger.V(4).Info("update workspace")
if err := r.Update(rootCtx, workspace); err != nil {
logger.Error(err, "update workspace failed")
return ctrl.Result{}, err
}
}
// Our finalizer has finished, so the reconciler can do nothing.
return reconcile.Result{}, nil
return ctrl.Result{}, nil
}
var namespaces corev1.NamespaceList
if err := r.List(rootCtx, &namespaces, client.MatchingLabels{tenantv1alpha1.WorkspaceLabel: req.Name}); err != nil {
logger.Error(err, "list namespaces failed")
return ctrl.Result{}, err
} else {
for _, namespace := range namespaces.Items {
if err := r.bindWorkspace(rootCtx, logger, &namespace, workspace); err != nil {
return ctrl.Result{}, err
}
}
}
return reconcile.Result{}, nil
r.Recorder.Event(workspace, corev1.EventTypeNormal, controllerutils.SuccessSynced, controllerutils.MessageResourceSynced)
return ctrl.Result{}, nil
}
func (r *Reconciler) bindWorkspace(ctx context.Context, logger logr.Logger, namespace *corev1.Namespace, workspace *tenantv1alpha1.Workspace) error {
// owner reference not match workspace label
if !metav1.IsControlledBy(namespace, workspace) {
namespace := namespace.DeepCopy()
namespace.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(namespace.OwnerReferences)
if err := controllerutil.SetControllerReference(workspace, namespace, scheme.Scheme); err != nil {
logger.Error(err, "set controller reference failed")
return err
}
logger.V(4).Info("update namespace owner reference", "workspace", workspace.Name)
if err := r.Update(ctx, namespace); err != nil {
logger.Error(err, "update namespace failed")
return err
}
}
return nil
}
......@@ -17,59 +17,84 @@ limitations under the License.
package workspace
import (
stdlog "log"
"github.com/onsi/gomega/gexec"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/klogr"
"kubesphere.io/kubesphere/pkg/apis"
"os"
"path/filepath"
"sync"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"testing"
"time"
"github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
)
var cfg *rest.Config
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var k8sClient client.Client
var k8sManager ctrl.Manager
var testEnv *envtest.Environment
func TestWorkspaceController(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Workspace Controller Test Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(klogr.New())
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
By("bootstrapping test environment")
t := true
if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" {
testEnv = &envtest.Environment{
UseExistingCluster: &t,
}
} else {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
AttachControlPlaneOutput: false,
}
}
code := m.Run()
t.Stop()
os.Exit(code)
}
cfg, err := testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = apis.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
MetricsBindAddress: "0",
})
return fn, requests
}
Expect(err).ToNot(HaveOccurred())
err = (&Reconciler{}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
err = k8sManager.Start(ctrl.SetupSignalHandler())
Expect(err).ToNot(HaveOccurred())
}()
return stop, wg
}
k8sClient = k8sManager.GetClient()
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
gexec.KillAndWait(5 * time.Second)
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
......@@ -15,3 +15,71 @@ limitations under the License.
*/
package workspace
import (
"context"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"time"
)
var _ = Describe("Workspace", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("Workspace Controller", func() {
It("Should create successfully", func() {
workspace := &tenantv1alpha1.Workspace{
ObjectMeta: metav1.ObjectMeta{
Name: "workspace-test",
},
}
// Create
Expect(k8sClient.Create(context.Background(), workspace)).Should(Succeed())
By("Expecting to create workspace successfully")
Eventually(func() bool {
f := &tenantv1alpha1.Workspace{}
k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, f)
return len(f.Finalizers) > 0
}, timeout, interval).Should(BeTrue())
// Update
updated := &tenantv1alpha1.Workspace{}
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, updated)).Should(Succeed())
updated.Spec.Manager = "admin"
Expect(k8sClient.Update(context.Background(), updated)).Should(Succeed())
// List workspace role bindings
By("Expecting to update workspace successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace)
return workspace.Spec.Manager == "admin"
}, timeout, interval).Should(BeTrue())
// Delete
By("Expecting to delete workspace successfully")
Eventually(func() error {
f := &tenantv1alpha1.Workspace{}
k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, f)
return k8sClient.Delete(context.Background(), f)
}, timeout, interval).Should(Succeed())
By("Expecting to delete workspace finish")
Eventually(func() error {
f := &tenantv1alpha1.Workspace{}
return k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, f)
}, timeout, interval).ShouldNot(Succeed())
})
})
})
/*
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"
tenantv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha2"
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
tenantv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/tenant/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 {
scheme *runtime.Scheme
k8sClient kubernetes.Interface
ksClient kubesphere.Interface
workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer
workspaceRoleLister iamv1alpha2listers.WorkspaceRoleLister
workspaceRoleSynced cache.InformerSynced
workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer
workspaceTemplateLister tenantv1alpha2listers.WorkspaceTemplateLister
workspaceTemplateSynced cache.InformerSynced
fedWorkspaceRoleCache cache.Store
fedWorkspaceRoleCacheController 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, workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer,
fedWorkspaceRoleCache cache.Store, fedWorkspaceRoleCacheController cache.Controller, workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer, 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,
workspaceRoleInformer: workspaceRoleInformer,
workspaceRoleLister: workspaceRoleInformer.Lister(),
workspaceRoleSynced: workspaceRoleInformer.Informer().HasSynced,
fedWorkspaceRoleCache: fedWorkspaceRoleCache,
fedWorkspaceRoleCacheController: fedWorkspaceRoleCacheController,
workspaceTemplateInformer: workspaceTemplateInformer,
workspaceTemplateLister: workspaceTemplateInformer.Lister(),
workspaceTemplateSynced: workspaceTemplateInformer.Informer().HasSynced,
multiClusterEnabled: multiClusterEnabled,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceRole"),
recorder: recorder,
}
klog.Info("Setting up event handlers")
workspaceRoleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.enqueueWorkspaceRole,
UpdateFunc: func(old, new interface{}) {
ctl.enqueueWorkspaceRole(new)
},
DeleteFunc: ctl.enqueueWorkspaceRole,
})
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 WorkspaceRole 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.workspaceRoleSynced, c.workspaceTemplateSynced)
if c.multiClusterEnabled {
synced = append(synced, c.fedWorkspaceRoleCacheController.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) enqueueWorkspaceRole(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.bindWorkspace(workspaceRole); err != nil {
klog.Error(err)
return err
}
if c.multiClusterEnabled {
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) bindWorkspace(workspaceRole *iamv1alpha2.WorkspaceRole) error {
workspaceName := workspaceRole.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return nil
}
workspace, err := c.workspaceTemplateLister.Get(workspaceName)
if err != nil {
// skip if workspace not found
if errors.IsNotFound(err) {
return nil
}
klog.Error(err)
return err
}
if !metav1.IsControlledBy(workspaceRole, workspace) {
workspaceRole.OwnerReferences = nil
if err := controllerutil.SetControllerReference(workspace, workspaceRole, scheme.Scheme); err != nil {
klog.Error(err)
return err
}
_, err = c.ksClient.IamV1alpha2().WorkspaceRoles().Update(workspaceRole)
if err != nil {
klog.Error(err)
return err
}
}
return nil
}
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
}
/*
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 (
"context"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
"kubesphere.io/kubesphere/pkg/constants"
controllerutils "kubesphere.io/kubesphere/pkg/controller/utils/controller"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"reflect"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
const (
controllerName = "workspacerole-controller"
)
// Reconciler reconciles a WorkspaceRole object
type Reconciler struct {
client.Client
MultiClusterEnabled bool
Logger logr.Logger
Scheme *runtime.Scheme
Recorder record.EventRecorder
MaxConcurrentReconciles int
}
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
if r.Client == nil {
r.Client = mgr.GetClient()
}
if r.Logger == nil {
r.Logger = ctrl.Log.WithName("controllers").WithName(controllerName)
}
if r.Scheme == nil {
r.Scheme = mgr.GetScheme()
}
if r.Recorder == nil {
r.Recorder = mgr.GetEventRecorderFor(controllerName)
}
if r.MaxConcurrentReconciles <= 0 {
r.MaxConcurrentReconciles = 1
}
return ctrl.NewControllerManagedBy(mgr).
Named(controllerName).
WithOptions(controller.Options{
MaxConcurrentReconciles: r.MaxConcurrentReconciles,
}).
For(&iamv1alpha2.WorkspaceRole{}).
Complete(r)
}
// +kubebuilder:rbac:groups=iam.kubesphere.io,resources=workspaceroles,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=types.kubefed.io,resources=federatedworkspaceroles,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
logger := r.Logger.WithValues("workspacerole", req.NamespacedName)
rootCtx := context.Background()
workspaceRole := &iamv1alpha2.WorkspaceRole{}
err := r.Get(rootCtx, req.NamespacedName, workspaceRole)
if err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// controlled kubefed-controller-manager
if workspaceRole.Labels[constants.KubefedManagedLabel] == "true" {
return ctrl.Result{}, nil
}
if err := r.bindWorkspace(rootCtx, logger, workspaceRole); err != nil {
return ctrl.Result{}, err
}
if r.MultiClusterEnabled {
if err = r.multiClusterSync(rootCtx, logger, workspaceRole); err != nil {
return ctrl.Result{}, err
}
}
r.Recorder.Event(workspaceRole, corev1.EventTypeNormal, controllerutils.SuccessSynced, controllerutils.MessageResourceSynced)
return ctrl.Result{}, nil
}
func (r *Reconciler) bindWorkspace(ctx context.Context, logger logr.Logger, workspaceRole *iamv1alpha2.WorkspaceRole) error {
workspaceName := workspaceRole.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return nil
}
var workspace tenantv1alpha2.WorkspaceTemplate
if err := r.Get(ctx, types.NamespacedName{Name: workspaceName}, &workspace); err != nil {
return client.IgnoreNotFound(err)
}
if !metav1.IsControlledBy(workspaceRole, &workspace) {
workspaceRole = workspaceRole.DeepCopy()
workspaceRole.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(workspaceRole.OwnerReferences)
if err := controllerutil.SetControllerReference(&workspace, workspaceRole, r.Scheme); err != nil {
logger.Error(err, "set controller reference failed")
return err
}
if err := r.Update(ctx, workspaceRole); err != nil {
logger.Error(err, "update workspace role failed")
return err
}
}
return nil
}
func (r *Reconciler) multiClusterSync(ctx context.Context, logger logr.Logger, workspaceRole *iamv1alpha2.WorkspaceRole) error {
if err := r.ensureNotControlledByKubefed(ctx, logger, workspaceRole); err != nil {
return err
}
federatedWorkspaceRole := &typesv1beta1.FederatedWorkspaceRole{}
if err := r.Client.Get(ctx, types.NamespacedName{Name: workspaceRole.Name}, federatedWorkspaceRole); err != nil {
if errors.IsNotFound(err) {
if federatedWorkspaceRole, err := newFederatedWorkspaceRole(workspaceRole); err != nil {
logger.Error(err, "create federated workspace role failed")
return err
} else {
if err := r.Create(ctx, federatedWorkspaceRole); err != nil {
logger.Error(err, "create federated workspace role failed")
return err
}
}
}
logger.Error(err, "get federated workspace role failed")
return err
}
if !reflect.DeepEqual(federatedWorkspaceRole.Spec.Template.Rules, workspaceRole.Rules) ||
!reflect.DeepEqual(federatedWorkspaceRole.Spec.Template.Labels, workspaceRole.Labels) {
federatedWorkspaceRole.Spec.Template.Rules = workspaceRole.Rules
federatedWorkspaceRole.Spec.Template.Labels = workspaceRole.Labels
if err := r.Update(ctx, federatedWorkspaceRole); err != nil {
logger.Error(err, "update federated workspace role failed")
return err
}
}
return nil
}
func newFederatedWorkspaceRole(workspaceRole *iamv1alpha2.WorkspaceRole) (*typesv1beta1.FederatedWorkspaceRole, error) {
federatedWorkspaceRole := &typesv1beta1.FederatedWorkspaceRole{
TypeMeta: metav1.TypeMeta{
Kind: typesv1beta1.FederatedWorkspaceRoleKind,
APIVersion: typesv1beta1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: workspaceRole.Name,
},
Spec: typesv1beta1.FederatedWorkspaceRoleSpec{
Template: typesv1beta1.WorkspaceRoleTemplate{
ObjectMeta: metav1.ObjectMeta{
Labels: workspaceRole.Labels,
},
Rules: workspaceRole.Rules,
},
Placement: typesv1beta1.GenericPlacementFields{
ClusterSelector: &metav1.LabelSelector{},
},
},
}
if err := controllerutil.SetControllerReference(workspaceRole, federatedWorkspaceRole, scheme.Scheme); err != nil {
return nil, err
}
return federatedWorkspaceRole, nil
}
func (r *Reconciler) ensureNotControlledByKubefed(ctx context.Context, logger logr.Logger, workspaceRole *iamv1alpha2.WorkspaceRole) error {
if workspaceRole.Labels[constants.KubefedManagedLabel] != "false" {
if workspaceRole.Labels == nil {
workspaceRole.Labels = make(map[string]string)
}
workspaceRole = workspaceRole.DeepCopy()
workspaceRole.Labels[constants.KubefedManagedLabel] = "false"
if err := r.Update(ctx, workspaceRole); err != nil {
logger.Error(err, "update kubefed managed label failed")
return err
}
}
return nil
}
/*
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 (
"github.com/onsi/gomega/gexec"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/klogr"
"kubesphere.io/kubesphere/pkg/apis"
"os"
"path/filepath"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var k8sClient client.Client
var k8sManager ctrl.Manager
var testEnv *envtest.Environment
func TestWorkspaceRoleController(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"WorkspaceRole Controller Test Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(klogr.New())
By("bootstrapping test environment")
t := true
if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" {
testEnv = &envtest.Environment{
UseExistingCluster: &t,
}
} else {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
AttachControlPlaneOutput: false,
}
}
cfg, err := testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = apis.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
MetricsBindAddress: "0",
})
Expect(err).ToNot(HaveOccurred())
err = (&Reconciler{}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
go func() {
err = k8sManager.Start(ctrl.SetupSignalHandler())
Expect(err).ToNot(HaveOccurred())
}()
k8sClient = k8sManager.GetClient()
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
gexec.KillAndWait(5 * time.Second)
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
/*
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 (
"context"
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"time"
)
var _ = Describe("WorkspaceRole", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
workspace := &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "workspace1",
},
}
BeforeEach(func() {
// Create workspace
Expect(k8sClient.Create(context.Background(), workspace)).Should(Succeed())
})
// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("WorkspaceRole Controller", func() {
It("Should create successfully", func() {
workspaceAdmin := &iamv1alpha2.WorkspaceRole{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-admin", workspace.Name),
Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: workspace.Name},
},
Rules: []rbacv1.PolicyRule{},
}
// Create workspace role
Expect(k8sClient.Create(context.Background(), workspaceAdmin)).Should(Succeed())
By("Expecting to create workspace role successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: workspaceAdmin.Name}, workspaceAdmin)
return !workspaceAdmin.CreationTimestamp.IsZero()
}, timeout, interval).Should(BeTrue())
By("Expecting to set owner reference successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: workspaceAdmin.Name}, workspaceAdmin)
return len(workspaceAdmin.OwnerReferences) > 0
}, timeout, interval).Should(BeTrue())
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace)).Should(Succeed())
controlled := true
expectedOwnerReference := metav1.OwnerReference{
Kind: workspace.Kind,
APIVersion: workspace.APIVersion,
UID: workspace.UID,
Name: workspace.Name,
Controller: &controlled,
BlockOwnerDeletion: &controlled,
}
By("Expecting to bind workspace successfully")
Expect(workspaceAdmin.OwnerReferences).To(ContainElement(expectedOwnerReference))
})
})
})
/*
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 (
"github.com/onsi/gomega/gexec"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/klogr"
"kubesphere.io/kubesphere/pkg/apis"
"os"
"path/filepath"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var k8sClient client.Client
var k8sManager ctrl.Manager
var testEnv *envtest.Environment
func TestWorkspaceRoleBindingController(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"WorkspaceRoleBinding Controller Test Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(klogr.New())
By("bootstrapping test environment")
t := true
if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" {
testEnv = &envtest.Environment{
UseExistingCluster: &t,
}
} else {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
AttachControlPlaneOutput: false,
}
}
cfg, err := testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = apis.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
MetricsBindAddress: "0",
})
Expect(err).ToNot(HaveOccurred())
err = (&Reconciler{}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
go func() {
err = k8sManager.Start(ctrl.SetupSignalHandler())
Expect(err).ToNot(HaveOccurred())
}()
k8sClient = k8sManager.GetClient()
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
gexec.KillAndWait(5 * time.Second)
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
/*
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 (
"context"
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"time"
)
var _ = Describe("WorkspaceRoleBinding", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
workspace := &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "workspace1",
},
}
BeforeEach(func() {
// Create workspace
Expect(k8sClient.Create(context.Background(), workspace)).Should(Succeed())
})
// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("WorkspaceRoleBinding Controller", func() {
It("Should create successfully", func() {
workspaceAdminBinding := &iamv1alpha2.WorkspaceRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("admin-workspace1-admin"),
Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: workspace.Name},
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1alpha2.SchemeGroupVersion.String(),
Kind: iamv1alpha2.FedWorkspaceRoleKind,
Name: "workspace1-admin",
},
}
// Create workspace role binding
Expect(k8sClient.Create(context.Background(), workspaceAdminBinding)).Should(Succeed())
By("Expecting to create workspace role successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: workspaceAdminBinding.Name}, workspaceAdminBinding)
return !workspaceAdminBinding.CreationTimestamp.IsZero()
}, timeout, interval).Should(BeTrue())
By("Expecting to set owner reference successfully")
Eventually(func() bool {
k8sClient.Get(context.Background(), types.NamespacedName{Name: workspaceAdminBinding.Name}, workspaceAdminBinding)
return len(workspaceAdminBinding.OwnerReferences) > 0
}, timeout, interval).Should(BeTrue())
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: workspace.Name}, workspace)).Should(Succeed())
controlled := true
expectedOwnerReference := metav1.OwnerReference{
Kind: workspace.Kind,
APIVersion: workspace.APIVersion,
UID: workspace.UID,
Name: workspace.Name,
Controller: &controlled,
BlockOwnerDeletion: &controlled,
}
By("Expecting to bind workspace successfully")
Expect(workspaceAdminBinding.OwnerReferences).To(ContainElement(expectedOwnerReference))
})
})
})
/*
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 (
"github.com/onsi/gomega/gexec"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/klogr"
"kubesphere.io/kubesphere/pkg/apis"
"os"
"path/filepath"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var k8sClient client.Client
var k8sManager ctrl.Manager
var testEnv *envtest.Environment
func TestWorkspaceTemplateController(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"WorkspaceTemplate Controller Test Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(klogr.New())
By("bootstrapping test environment")
t := true
if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" {
testEnv = &envtest.Environment{
UseExistingCluster: &t,
}
} else {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
AttachControlPlaneOutput: false,
}
}
cfg, err := testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = apis.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
MetricsBindAddress: "0",
})
Expect(err).ToNot(HaveOccurred())
err = (&Reconciler{}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
go func() {
err = k8sManager.Start(ctrl.SetupSignalHandler())
Expect(err).ToNot(HaveOccurred())
}()
k8sClient = k8sManager.GetClient()
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
gexec.KillAndWait(5 * time.Second)
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
/*
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 (
"context"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"sigs.k8s.io/controller-runtime/pkg/client"
"time"
)
var _ = Describe("WorkspaceTemplate", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
BeforeEach(func() {
workspaceAdmin := newWorkspaceAdmin()
err := k8sClient.Create(context.Background(), &workspaceAdmin)
Expect(err).NotTo(HaveOccurred())
admin := iamv1alpha2.User{ObjectMeta: metav1.ObjectMeta{Name: "admin"}}
err = k8sClient.Create(context.Background(), &admin)
Expect(err).NotTo(HaveOccurred())
})
// Add Tests for OpenAPI validation (or additonal CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("WorkspaceTemplate Controller", func() {
It("Should create successfully", func() {
key := types.NamespacedName{
Name: "workspace-template",
}
created := &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: key.Name,
},
}
// Create
Expect(k8sClient.Create(context.Background(), created)).Should(Succeed())
By("Expecting to create workspace template successfully")
Eventually(func() bool {
f := &tenantv1alpha2.WorkspaceTemplate{}
k8sClient.Get(context.Background(), key, f)
return !f.CreationTimestamp.IsZero()
}, timeout, interval).Should(BeTrue())
By("Expecting to create workspace successfully")
Eventually(func() bool {
f := &tenantv1alpha1.Workspace{}
k8sClient.Get(context.Background(), key, f)
return !f.CreationTimestamp.IsZero()
}, timeout, interval).Should(BeTrue())
// List workspace roles
By("Expecting to create workspace role successfully")
Eventually(func() bool {
f := &iamv1alpha2.WorkspaceRoleList{}
k8sClient.List(context.Background(), f, &client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{tenantv1alpha1.WorkspaceLabel: key.Name})})
return len(f.Items) == 1
}, timeout, interval).Should(BeTrue())
// Update
updated := &tenantv1alpha2.WorkspaceTemplate{}
Expect(k8sClient.Get(context.Background(), key, updated)).Should(Succeed())
updated.Spec.Template.Spec.Manager = "admin"
Expect(k8sClient.Update(context.Background(), updated)).Should(Succeed())
// List workspace role bindings
By("Expecting to create workspace manager role binding successfully")
Eventually(func() bool {
f := &iamv1alpha2.WorkspaceRoleBindingList{}
k8sClient.List(context.Background(), f, &client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{tenantv1alpha1.WorkspaceLabel: key.Name})})
return len(f.Items) == 1
}, timeout, interval).Should(BeTrue())
// Delete
By("Expecting to delete workspace successfully")
Eventually(func() error {
f := &tenantv1alpha2.WorkspaceTemplate{}
k8sClient.Get(context.Background(), key, f)
return k8sClient.Delete(context.Background(), f)
}, timeout, interval).Should(Succeed())
By("Expecting to delete workspace finish")
Eventually(func() error {
f := &tenantv1alpha2.WorkspaceTemplate{}
return k8sClient.Get(context.Background(), key, f)
}, timeout, interval).ShouldNot(Succeed())
})
})
})
func newWorkspaceAdmin() iamv1alpha2.RoleBase {
return iamv1alpha2.RoleBase{
ObjectMeta: metav1.ObjectMeta{Name: "workspace-admin"},
Role: runtime.RawExtension{
Raw: []byte(`{
"apiVersion": "iam.kubesphere.io/v1alpha2",
"kind": "WorkspaceRole",
"metadata": {
"name": "admin"
},
"rules": [
{
"apiGroups": [
"*"
],
"resources": [
"*"
],
"verbs": [
"*"
]
}
]
}`)}}
}
......@@ -18,13 +18,28 @@ package k8sutil
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
)
func IsControlledBy(reference []metav1.OwnerReference, kind string, name string) bool {
for _, ref := range reference {
if ref.Kind == kind && (name == "" || ref.Name == name) {
// IsControlledBy returns whether the ownerReferences contains the specified resource kind
func IsControlledBy(ownerReferences []metav1.OwnerReference, kind string, name string) bool {
for _, owner := range ownerReferences {
if owner.Kind == kind && (name == "" || owner.Name == name) {
return true
}
}
return false
}
// RemoveWorkspaceOwnerReference remove workspace kind owner reference
func RemoveWorkspaceOwnerReference(ownerReferences []metav1.OwnerReference) []metav1.OwnerReference {
tmp := make([]metav1.OwnerReference, 0)
for _, owner := range ownerReferences {
if owner.Kind != tenantv1alpha1.ResourceKindWorkspace &&
owner.Kind != tenantv1alpha2.ResourceKindWorkspaceTemplate {
tmp = append(tmp, owner)
}
}
return tmp
}
/*
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 k8sutil
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"reflect"
"testing"
)
func TestIsControlledBy(t *testing.T) {
type args struct {
ownerReferences []metav1.OwnerReference
kind string
name string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "controlled by Workspace",
args: args{
ownerReferences: []metav1.OwnerReference{{
APIVersion: tenantv1alpha1.SchemeGroupVersion.String(),
Kind: tenantv1alpha1.ResourceKindWorkspace,
Name: "workspace-test",
}},
kind: tenantv1alpha1.ResourceKindWorkspace,
},
want: true,
},
{
name: "controlled by workspace-test",
args: args{
ownerReferences: []metav1.OwnerReference{{
APIVersion: tenantv1alpha1.SchemeGroupVersion.String(),
Kind: tenantv1alpha1.ResourceKindWorkspace,
Name: "workspace-test",
}},
kind: tenantv1alpha1.ResourceKindWorkspace,
name: "workspace-test",
},
want: true,
},
{
name: "not controlled by workspace-test",
args: args{
ownerReferences: []metav1.OwnerReference{{
APIVersion: tenantv1alpha1.SchemeGroupVersion.String(),
Kind: tenantv1alpha1.ResourceKindWorkspace,
Name: "workspace",
}},
kind: tenantv1alpha1.ResourceKindWorkspace,
name: "workspace-test",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsControlledBy(tt.args.ownerReferences, tt.args.kind, tt.args.name); got != tt.want {
t.Errorf("IsControlledBy() = %v, want %v", got, tt.want)
}
})
}
}
func TestRemoveWorkspaceOwnerReference(t *testing.T) {
type args struct {
ownerReferences []metav1.OwnerReference
}
tests := []struct {
name string
args args
want []metav1.OwnerReference
}{
{
name: "remove workspace owner reference",
args: args{ownerReferences: []metav1.OwnerReference{{
APIVersion: tenantv1alpha1.SchemeGroupVersion.String(),
Kind: tenantv1alpha1.ResourceKindWorkspace,
Name: "workspace-test",
}}},
want: []metav1.OwnerReference{},
},
{
name: "remove workspace owner reference",
args: args{ownerReferences: []metav1.OwnerReference{{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Namespace",
Name: "namespace",
}}},
want: []metav1.OwnerReference{{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Namespace",
Name: "namespace",
}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := RemoveWorkspaceOwnerReference(tt.args.ownerReferences); !reflect.DeepEqual(got, tt.want) {
t.Errorf("RemoveWorkspaceOwnerReference() = %v, want %v", got, tt.want)
}
})
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册