Merge branch 'dev' into devops-refactor
# Conflicts: # cmd/controller-manager/app/controllers.go # hack/generate_client.sh # pkg/client/clientset/versioned/clientset.go # pkg/client/clientset/versioned/fake/clientset_generated.go # pkg/client/clientset/versioned/fake/register.go # pkg/client/clientset/versioned/scheme/register.go # pkg/client/informers/externalversions/generic.go
This commit is contained in:
@@ -28,6 +28,7 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/controller/s2ibinary"
|
"kubesphere.io/kubesphere/pkg/controller/s2ibinary"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/s2irun"
|
"kubesphere.io/kubesphere/pkg/controller/s2irun"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/storage/expansion"
|
"kubesphere.io/kubesphere/pkg/controller/storage/expansion"
|
||||||
|
"kubesphere.io/kubesphere/pkg/controller/user"
|
||||||
"kubesphere.io/kubesphere/pkg/controller/virtualservice"
|
"kubesphere.io/kubesphere/pkg/controller/virtualservice"
|
||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
||||||
@@ -111,6 +112,11 @@ func AddControllers(
|
|||||||
kubernetesInformer.Apps().V1().ReplicaSets(),
|
kubernetesInformer.Apps().V1().ReplicaSets(),
|
||||||
kubernetesInformer.Apps().V1().StatefulSets())
|
kubernetesInformer.Apps().V1().StatefulSets())
|
||||||
|
|
||||||
|
userController := user.NewController(
|
||||||
|
client.Kubernetes(),
|
||||||
|
client.KubeSphere(),
|
||||||
|
kubesphereInformer.Iam().V1alpha2().Users())
|
||||||
|
|
||||||
controllers := map[string]manager.Runnable{
|
controllers := map[string]manager.Runnable{
|
||||||
"virtualservice-controller": vsController,
|
"virtualservice-controller": vsController,
|
||||||
"destinationrule-controller": drController,
|
"destinationrule-controller": drController,
|
||||||
@@ -122,6 +128,7 @@ func AddControllers(
|
|||||||
"devopsprojects-controller": devopsProjectController,
|
"devopsprojects-controller": devopsProjectController,
|
||||||
"pipeline-controller": devopsPipelineController,
|
"pipeline-controller": devopsPipelineController,
|
||||||
"devopscredential-controller": devopsCredentialController,
|
"devopscredential-controller": devopsCredentialController,
|
||||||
|
"user-controller": userController,
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, ctrl := range controllers {
|
for name, ctrl := range controllers {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
kubesphereconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
|
kubesphereconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
|
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -21,8 +20,6 @@ type KubeSphereControllerManagerOptions struct {
|
|||||||
DevopsOptions *jenkins.Options
|
DevopsOptions *jenkins.Options
|
||||||
S3Options *s3.Options
|
S3Options *s3.Options
|
||||||
OpenPitrixOptions *openpitrix.Options
|
OpenPitrixOptions *openpitrix.Options
|
||||||
KubeSphereOptions *kubesphere.Options
|
|
||||||
|
|
||||||
LeaderElection *leaderelection.LeaderElectionConfig
|
LeaderElection *leaderelection.LeaderElectionConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +29,6 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions
|
|||||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||||
S3Options: s3.NewS3Options(),
|
S3Options: s3.NewS3Options(),
|
||||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||||
KubeSphereOptions: kubesphere.NewKubeSphereOptions(),
|
|
||||||
LeaderElection: &leaderelection.LeaderElectionConfig{
|
LeaderElection: &leaderelection.LeaderElectionConfig{
|
||||||
LeaseDuration: 30 * time.Second,
|
LeaseDuration: 30 * time.Second,
|
||||||
RenewDeadline: 15 * time.Second,
|
RenewDeadline: 15 * time.Second,
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
kclient "kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
|
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
||||||
"kubesphere.io/kubesphere/pkg/utils/term"
|
"kubesphere.io/kubesphere/pkg/utils/term"
|
||||||
@@ -52,11 +51,13 @@ func NewControllerManagerCommand() *cobra.Command {
|
|||||||
s := options.NewKubeSphereControllerManagerOptions()
|
s := options.NewKubeSphereControllerManagerOptions()
|
||||||
conf, err := controllerconfig.TryLoadFromDisk()
|
conf, err := controllerconfig.TryLoadFromDisk()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
// make sure LeaderElection is not nil
|
||||||
s = &options.KubeSphereControllerManagerOptions{
|
s = &options.KubeSphereControllerManagerOptions{
|
||||||
KubernetesOptions: conf.KubernetesOptions,
|
KubernetesOptions: conf.KubernetesOptions,
|
||||||
DevopsOptions: conf.DevopsOptions,
|
DevopsOptions: conf.DevopsOptions,
|
||||||
S3Options: conf.S3Options,
|
S3Options: conf.S3Options,
|
||||||
OpenPitrixOptions: conf.OpenPitrixOptions,
|
OpenPitrixOptions: conf.OpenPitrixOptions,
|
||||||
|
LeaderElection: s.LeaderElection,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,8 +99,6 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
kubesphereClient := kclient.NewKubeSphereClient(s.KubeSphereOptions)
|
|
||||||
|
|
||||||
openpitrixClient, err := openpitrix.NewClient(s.OpenPitrixOptions)
|
openpitrixClient, err := openpitrix.NewClient(s.OpenPitrixOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Failed to create openpitrix client %v", err)
|
klog.Errorf("Failed to create openpitrix client %v", err)
|
||||||
@@ -119,7 +118,6 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
|||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := informers.NewInformerFactories(kubernetesClient.Kubernetes(), kubernetesClient.KubeSphere(), kubernetesClient.Istio(), kubernetesClient.Application())
|
informerFactory := informers.NewInformerFactories(kubernetesClient.Kubernetes(), kubernetesClient.KubeSphere(), kubernetesClient.Istio(), kubernetesClient.Application())
|
||||||
informerFactory.Start(stopCh)
|
|
||||||
|
|
||||||
run := func(ctx context.Context) {
|
run := func(ctx context.Context) {
|
||||||
klog.V(0).Info("setting up manager")
|
klog.V(0).Info("setting up manager")
|
||||||
@@ -134,7 +132,7 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
|||||||
}
|
}
|
||||||
|
|
||||||
klog.V(0).Info("Setting up controllers")
|
klog.V(0).Info("Setting up controllers")
|
||||||
err = workspace.Add(mgr, kubesphereClient)
|
err = workspace.Add(mgr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Fatal("Unable to create workspace controller")
|
klog.Fatal("Unable to create workspace controller")
|
||||||
}
|
}
|
||||||
@@ -148,6 +146,9 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
|||||||
klog.Fatalf("unable to register controllers to the manager: %v", err)
|
klog.Fatalf("unable to register controllers to the manager: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start cache data after all informer is registered
|
||||||
|
informerFactory.Start(stopCh)
|
||||||
|
|
||||||
klog.V(0).Info("Starting the controllers.")
|
klog.V(0).Info("Starting the controllers.")
|
||||||
if err = mgr.Start(stopCh); err != nil {
|
if err = mgr.Start(stopCh); err != nil {
|
||||||
klog.Fatalf("unable to run the manager: %v", err)
|
klog.Fatalf("unable to run the manager: %v", err)
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
cliflag "k8s.io/component-base/cli/flag"
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver"
|
"kubesphere.io/kubesphere/pkg/apiserver"
|
||||||
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
|
apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
|
||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
genericoptions "kubesphere.io/kubesphere/pkg/server/options"
|
genericoptions "kubesphere.io/kubesphere/pkg/server/options"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||||
@@ -29,27 +30,16 @@ import (
|
|||||||
type ServerRunOptions struct {
|
type ServerRunOptions struct {
|
||||||
ConfigFile string
|
ConfigFile string
|
||||||
GenericServerRunOptions *genericoptions.ServerRunOptions
|
GenericServerRunOptions *genericoptions.ServerRunOptions
|
||||||
KubernetesOptions *k8s.KubernetesOptions
|
*apiserverconfig.Config
|
||||||
DevopsOptions *jenkins.Options
|
|
||||||
SonarQubeOptions *sonarqube.Options
|
|
||||||
ServiceMeshOptions *servicemesh.Options
|
|
||||||
MySQLOptions *mysql.Options
|
|
||||||
MonitoringOptions *prometheus.Options
|
|
||||||
S3Options *s3.Options
|
|
||||||
OpenPitrixOptions *openpitrix.Options
|
|
||||||
LoggingOptions *esclient.Options
|
|
||||||
LdapOptions *ldap.Options
|
|
||||||
CacheOptions *cache.Options
|
|
||||||
AuthenticateOptions *auth.AuthenticationOptions
|
|
||||||
|
|
||||||
//
|
//
|
||||||
DebugMode bool
|
DebugMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerRunOptions() *ServerRunOptions {
|
func NewServerRunOptions() *ServerRunOptions {
|
||||||
|
s := &ServerRunOptions{
|
||||||
s := ServerRunOptions{
|
|
||||||
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
|
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
|
||||||
|
Config: &apiserverconfig.Config{
|
||||||
KubernetesOptions: k8s.NewKubernetesOptions(),
|
KubernetesOptions: k8s.NewKubernetesOptions(),
|
||||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||||
SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
|
SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
|
||||||
@@ -60,11 +50,12 @@ func NewServerRunOptions() *ServerRunOptions {
|
|||||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||||
LoggingOptions: esclient.NewElasticSearchOptions(),
|
LoggingOptions: esclient.NewElasticSearchOptions(),
|
||||||
LdapOptions: ldap.NewOptions(),
|
LdapOptions: ldap.NewOptions(),
|
||||||
CacheOptions: cache.NewRedisOptions(),
|
RedisOptions: cache.NewRedisOptions(),
|
||||||
AuthenticateOptions: auth.NewAuthenticateOptions(),
|
AuthenticationOptions: authoptions.NewAuthenticateOptions(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
||||||
@@ -72,12 +63,12 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
|||||||
fs.BoolVar(&s.DebugMode, "debug", false, "Don't enable this if you don't know what it means.")
|
fs.BoolVar(&s.DebugMode, "debug", false, "Don't enable this if you don't know what it means.")
|
||||||
s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions)
|
s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions)
|
||||||
s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions)
|
s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions)
|
||||||
s.AuthenticateOptions.AddFlags(fss.FlagSet("authenticate"), s.AuthenticateOptions)
|
s.AuthenticationOptions.AddFlags(fss.FlagSet("authentication"), s.AuthenticationOptions)
|
||||||
s.MySQLOptions.AddFlags(fss.FlagSet("mysql"), s.MySQLOptions)
|
s.MySQLOptions.AddFlags(fss.FlagSet("mysql"), s.MySQLOptions)
|
||||||
s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
|
s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
|
||||||
s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions)
|
s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions)
|
||||||
s.LdapOptions.AddFlags(fss.FlagSet("ldap"), s.LdapOptions)
|
s.LdapOptions.AddFlags(fss.FlagSet("ldap"), s.LdapOptions)
|
||||||
s.CacheOptions.AddFlags(fss.FlagSet("cache"), s.CacheOptions)
|
s.RedisOptions.AddFlags(fss.FlagSet("redis"), s.RedisOptions)
|
||||||
s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options)
|
s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options)
|
||||||
s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
|
s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
|
||||||
s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh"), s.ServiceMeshOptions)
|
s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh"), s.ServiceMeshOptions)
|
||||||
@@ -100,7 +91,7 @@ const fakeInterface string = "FAKE"
|
|||||||
// NewAPIServer creates an APIServer instance using given options
|
// NewAPIServer creates an APIServer instance using given options
|
||||||
func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIServer, error) {
|
func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIServer, error) {
|
||||||
apiServer := &apiserver.APIServer{
|
apiServer := &apiserver.APIServer{
|
||||||
AuthenticateOptions: s.AuthenticateOptions,
|
Config: s.Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
kubernetesClient, err := k8s.NewKubernetesClient(s.KubernetesOptions)
|
kubernetesClient, err := k8s.NewKubernetesClient(s.KubernetesOptions)
|
||||||
@@ -156,11 +147,11 @@ func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIS
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cacheClient cache.Interface
|
var cacheClient cache.Interface
|
||||||
if s.CacheOptions.Host != "" {
|
if s.RedisOptions.Host != "" {
|
||||||
if s.CacheOptions.Host == fakeInterface && s.DebugMode {
|
if s.RedisOptions.Host == fakeInterface && s.DebugMode {
|
||||||
apiServer.CacheClient = cache.NewSimpleCache()
|
apiServer.CacheClient = cache.NewSimpleCache()
|
||||||
} else {
|
} else {
|
||||||
cacheClient, err = cache.NewRedisClient(s.CacheOptions, stopCh)
|
cacheClient, err = cache.NewRedisClient(s.RedisOptions, stopCh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,18 +37,7 @@ func NewAPIServerCommand() *cobra.Command {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
s = &options.ServerRunOptions{
|
s = &options.ServerRunOptions{
|
||||||
GenericServerRunOptions: s.GenericServerRunOptions,
|
GenericServerRunOptions: s.GenericServerRunOptions,
|
||||||
KubernetesOptions: conf.KubernetesOptions,
|
Config: conf,
|
||||||
DevopsOptions: conf.DevopsOptions,
|
|
||||||
SonarQubeOptions: conf.SonarQubeOptions,
|
|
||||||
ServiceMeshOptions: conf.ServiceMeshOptions,
|
|
||||||
MySQLOptions: conf.MySQLOptions,
|
|
||||||
MonitoringOptions: conf.MonitoringOptions,
|
|
||||||
S3Options: conf.S3Options,
|
|
||||||
OpenPitrixOptions: conf.OpenPitrixOptions,
|
|
||||||
LoggingOptions: conf.LoggingOptions,
|
|
||||||
LdapOptions: conf.LdapOptions,
|
|
||||||
CacheOptions: conf.RedisOptions,
|
|
||||||
AuthenticateOptions: conf.AuthenticateOptions,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
117
config/crds/iam.kubesphere.io_users.yaml
generated
Normal file
117
config/crds/iam.kubesphere.io_users.yaml
generated
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: (devel)
|
||||||
|
creationTimestamp: null
|
||||||
|
name: users.iam.kubesphere.io
|
||||||
|
spec:
|
||||||
|
additionalPrinterColumns:
|
||||||
|
- JSONPath: .spec.email
|
||||||
|
name: Email
|
||||||
|
type: string
|
||||||
|
- JSONPath: .status.state
|
||||||
|
name: Status
|
||||||
|
type: string
|
||||||
|
group: iam.kubesphere.io
|
||||||
|
names:
|
||||||
|
categories:
|
||||||
|
- iam
|
||||||
|
kind: User
|
||||||
|
listKind: UserList
|
||||||
|
plural: users
|
||||||
|
singular: user
|
||||||
|
scope: Cluster
|
||||||
|
subresources: {}
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: User is the Schema for the users API
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: UserSpec defines the desired state of User
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
description: Description of the user.
|
||||||
|
type: string
|
||||||
|
displayName:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
description: Unique email address.
|
||||||
|
type: string
|
||||||
|
finalizers:
|
||||||
|
description: Finalizers is an opaque list of values that must be empty
|
||||||
|
to permanently remove object from storage.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
groups:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
lang:
|
||||||
|
description: The preferred written or spoken language for the user.
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- email
|
||||||
|
- password
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: UserStatus defines the observed state of User
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
description: Represents the latest available observations of a namespace's
|
||||||
|
current state.
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
lastTransitionTime:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
reason:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
description: Status of the condition, one of True, False, Unknown.
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: Type of namespace controller condition.
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
state:
|
||||||
|
description: The user status
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- spec
|
||||||
|
type: object
|
||||||
|
version: v1alpha2
|
||||||
|
versions:
|
||||||
|
- name: v1alpha2
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
9
config/samples/iam_v1alpha2_user.yaml
Normal file
9
config/samples/iam_v1alpha2_user.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: iam.kubesphere.io/v1alpha2
|
||||||
|
kind: User
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
controller-tools.k8s.io: "1.0"
|
||||||
|
name: admin
|
||||||
|
spec:
|
||||||
|
email: admin@kubesphere.io
|
||||||
|
password: d41d8cd98f00b204e9800998ecf8427e
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 devops:v1alpha1 devops:v1alpha3 tower:v1alpha1"
|
GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 devops:v1alpha1 devops:v1alpha3 iam:v1alpha2 tower:v1alpha1"
|
||||||
|
|
||||||
rm -rf ./pkg/client
|
rm -rf ./pkg/client
|
||||||
./hack/generate_group.sh "client,lister,informer" kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "$GV" --output-base=./ -h "$PWD/hack/boilerplate.go.txt"
|
./hack/generate_group.sh "client,lister,informer" kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "$GV" --output-base=./ -h "$PWD/hack/boilerplate.go.txt"
|
||||||
|
|||||||
26
pkg/apis/addtoscheme_iam_v1alpha2.go
Normal file
26
pkg/apis/addtoscheme_iam_v1alpha2.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
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 apis
|
||||||
|
|
||||||
|
import (
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
|
||||||
|
AddToSchemes = append(AddToSchemes, iamv1alpha2.SchemeBuilder.AddToScheme)
|
||||||
|
}
|
||||||
18
pkg/apis/iam/group.go
Normal file
18
pkg/apis/iam/group.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
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 iam contains iam API versions
|
||||||
|
package iam
|
||||||
23
pkg/apis/iam/v1alpha2/doc.go
Normal file
23
pkg/apis/iam/v1alpha2/doc.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
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 v1alpha2 contains API Schema definitions for the iam v1alpha2 API group
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
// +k8s:deepcopy-gen=package,register
|
||||||
|
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/iam
|
||||||
|
// +k8s:defaulter-gen=TypeMeta
|
||||||
|
// +groupName=iam.kubesphere.io
|
||||||
|
package v1alpha2
|
||||||
46
pkg/apis/iam/v1alpha2/register.go
Normal file
46
pkg/apis/iam/v1alpha2/register.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// NOTE: Boilerplate only. Ignore this file.
|
||||||
|
|
||||||
|
// Package v1alpha2 contains API Schema definitions for the iam v1alpha2 API group
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
// +k8s:deepcopy-gen=package,register
|
||||||
|
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/iam
|
||||||
|
// +k8s:defaulter-gen=TypeMeta
|
||||||
|
// +groupName=iam.kubesphere.io
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/runtime/scheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// SchemeGroupVersion is group version used to register these objects
|
||||||
|
SchemeGroupVersion = schema.GroupVersion{Group: "iam.kubesphere.io", Version: "v1alpha2"}
|
||||||
|
|
||||||
|
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||||
|
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
|
||||||
|
|
||||||
|
// AddToScheme is required by pkg/client/...
|
||||||
|
AddToScheme = SchemeBuilder.AddToScheme
|
||||||
|
)
|
||||||
|
|
||||||
|
// Resource is required by pkg/client/listers/...
|
||||||
|
func Resource(resource string) schema.GroupResource {
|
||||||
|
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||||
|
}
|
||||||
133
pkg/apis/iam/v1alpha2/user_types.go
Normal file
133
pkg/apis/iam/v1alpha2/user_types.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
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 v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
// User is the Schema for the users API
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
// +genclient
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
// +genclient:nonNamespaced
|
||||||
|
// +kubebuilder:printcolumn:name="Email",type="string",JSONPath=".spec.email"
|
||||||
|
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.state"
|
||||||
|
// +kubebuilder:resource:categories="iam",scope="Cluster"
|
||||||
|
type User struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec UserSpec `json:"spec"`
|
||||||
|
// +optional
|
||||||
|
Status UserStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FinalizerName string
|
||||||
|
|
||||||
|
// UserSpec defines the desired state of User
|
||||||
|
type UserSpec struct {
|
||||||
|
// Unique email address.
|
||||||
|
Email string `json:"email"`
|
||||||
|
// The preferred written or spoken language for the user.
|
||||||
|
// +optional
|
||||||
|
Lang string `json:"lang,omitempty"`
|
||||||
|
// Description of the user.
|
||||||
|
// +optional
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
// +optional
|
||||||
|
DisplayName string `json:"displayName,omitempty"`
|
||||||
|
// +optional
|
||||||
|
Groups []string `json:"groups,omitempty"`
|
||||||
|
EncryptedPassword string `json:"password"`
|
||||||
|
|
||||||
|
// Finalizers is an opaque list of values that must be empty to permanently remove object from storage.
|
||||||
|
// +optional
|
||||||
|
Finalizers []FinalizerName `json:"finalizers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserState string
|
||||||
|
|
||||||
|
// These are the valid phases of a user.
|
||||||
|
const (
|
||||||
|
// UserActive means the user is available.
|
||||||
|
UserActive UserState = "Active"
|
||||||
|
// UserDisabled means the user is disabled.
|
||||||
|
UserDisabled UserState = "Disabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserStatus defines the observed state of User
|
||||||
|
type UserStatus struct {
|
||||||
|
// The user status
|
||||||
|
// +optional
|
||||||
|
State UserState `json:"state,omitempty"`
|
||||||
|
|
||||||
|
// Represents the latest available observations of a namespace's current state.
|
||||||
|
// +optional
|
||||||
|
// +patchMergeKey=type
|
||||||
|
// +patchStrategy=merge
|
||||||
|
Conditions []UserCondition `json:"conditions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserCondition struct {
|
||||||
|
// Type of namespace controller condition.
|
||||||
|
Type UserConditionType `json:"type"`
|
||||||
|
// Status of the condition, one of True, False, Unknown.
|
||||||
|
Status ConditionStatus `json:"status"`
|
||||||
|
// +optional
|
||||||
|
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
|
||||||
|
// +optional
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
// +optional
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserConditionType string
|
||||||
|
|
||||||
|
// These are valid conditions of a user.
|
||||||
|
const (
|
||||||
|
// UserLoginFailure contains information about user login.
|
||||||
|
UserLoginFailure UserConditionType = "UserLoginFailure"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConditionStatus string
|
||||||
|
|
||||||
|
// These are valid condition statuses. "ConditionTrue" means a resource is in the condition.
|
||||||
|
// "ConditionFalse" means a resource is not in the condition. "ConditionUnknown" means kubernetes
|
||||||
|
// can't decide if a resource is in the condition or not. In the future, we could add other
|
||||||
|
// intermediate conditions, e.g. ConditionDegraded.
|
||||||
|
const (
|
||||||
|
ConditionTrue ConditionStatus = "True"
|
||||||
|
ConditionFalse ConditionStatus = "False"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
// +genclient:nonNamespaced
|
||||||
|
|
||||||
|
// UserList contains a list of User
|
||||||
|
type UserList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []User `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&User{}, &UserList{})
|
||||||
|
}
|
||||||
58
pkg/apis/iam/v1alpha2/user_types_test.go
Normal file
58
pkg/apis/iam/v1alpha2/user_types_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2020 The KubeSphere Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* /
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/onsi/gomega"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStorageUser(t *testing.T) {
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: "foo",
|
||||||
|
}
|
||||||
|
created := &User{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
}}
|
||||||
|
g := gomega.NewGomegaWithT(t)
|
||||||
|
|
||||||
|
// Test Create
|
||||||
|
fetched := &User{}
|
||||||
|
g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed())
|
||||||
|
|
||||||
|
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
|
||||||
|
g.Expect(fetched).To(gomega.Equal(created))
|
||||||
|
|
||||||
|
// Test Updating the Labels
|
||||||
|
updated := fetched.DeepCopy()
|
||||||
|
updated.Labels = map[string]string{"hello": "world"}
|
||||||
|
g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed())
|
||||||
|
|
||||||
|
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
|
||||||
|
g.Expect(fetched).To(gomega.Equal(updated))
|
||||||
|
|
||||||
|
// Test Delete
|
||||||
|
g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed())
|
||||||
|
g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed())
|
||||||
|
}
|
||||||
55
pkg/apis/iam/v1alpha2/v1alpha2_suite_test.go
Normal file
55
pkg/apis/iam/v1alpha2/v1alpha2_suite_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
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 v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cfg *rest.Config
|
||||||
|
var c client.Client
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
t := &envtest.Environment{
|
||||||
|
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg, err = t.Start(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
code := m.Run()
|
||||||
|
t.Stop()
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
147
pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go
generated
Normal file
147
pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go
generated
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by controller-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *User) DeepCopyInto(out *User) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User.
|
||||||
|
func (in *User) DeepCopy() *User {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(User)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *User) 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 *UserCondition) DeepCopyInto(out *UserCondition) {
|
||||||
|
*out = *in
|
||||||
|
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserCondition.
|
||||||
|
func (in *UserCondition) DeepCopy() *UserCondition {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(UserCondition)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *UserList) DeepCopyInto(out *UserList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]User, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserList.
|
||||||
|
func (in *UserList) DeepCopy() *UserList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(UserList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *UserList) 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 *UserSpec) DeepCopyInto(out *UserSpec) {
|
||||||
|
*out = *in
|
||||||
|
if in.Groups != nil {
|
||||||
|
in, out := &in.Groups, &out.Groups
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Finalizers != nil {
|
||||||
|
in, out := &in.Finalizers, &out.Finalizers
|
||||||
|
*out = make([]FinalizerName, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec.
|
||||||
|
func (in *UserSpec) DeepCopy() *UserSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(UserSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *UserStatus) DeepCopyInto(out *UserStatus) {
|
||||||
|
*out = *in
|
||||||
|
if in.Conditions != nil {
|
||||||
|
in, out := &in.Conditions, &out.Conditions
|
||||||
|
*out = make([]UserCondition, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserStatus.
|
||||||
|
func (in *UserStatus) DeepCopy() *UserStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(UserStatus)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
@@ -12,21 +12,19 @@ import (
|
|||||||
unionauth "k8s.io/apiserver/pkg/authentication/request/union"
|
unionauth "k8s.io/apiserver/pkg/authentication/request/union"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwttoken"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwttoken"
|
||||||
oauth2 "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/request/anonymous"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/request/anonymous"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/request/basictoken"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/request/basictoken"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory"
|
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/path"
|
"kubesphere.io/kubesphere/pkg/apiserver/authorization/path"
|
||||||
unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union"
|
unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union"
|
||||||
|
apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/dispatch"
|
"kubesphere.io/kubesphere/pkg/apiserver/dispatch"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/filters"
|
"kubesphere.io/kubesphere/pkg/apiserver/filters"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2"
|
|
||||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2"
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2"
|
||||||
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2"
|
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2"
|
||||||
monitoringv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha2"
|
monitoringv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha2"
|
||||||
@@ -35,8 +33,8 @@ import (
|
|||||||
operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2"
|
operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2"
|
||||||
resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2"
|
resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2"
|
||||||
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha3"
|
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha3"
|
||||||
|
"kubesphere.io/kubesphere/pkg/kapis/serverconfig/v1alpha2"
|
||||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2"
|
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2"
|
||||||
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2"
|
|
||||||
terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2"
|
terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2"
|
||||||
"kubesphere.io/kubesphere/pkg/models/iam/am"
|
"kubesphere.io/kubesphere/pkg/models/iam/am"
|
||||||
"kubesphere.io/kubesphere/pkg/models/iam/im"
|
"kubesphere.io/kubesphere/pkg/models/iam/im"
|
||||||
@@ -75,7 +73,7 @@ type APIServer struct {
|
|||||||
//
|
//
|
||||||
Server *http.Server
|
Server *http.Server
|
||||||
|
|
||||||
AuthenticateOptions *auth.AuthenticationOptions
|
Config *apiserverconfig.Config
|
||||||
|
|
||||||
// webservice container, where all webservice defines
|
// webservice container, where all webservice defines
|
||||||
container *restful.Container
|
container *restful.Container
|
||||||
@@ -135,19 +133,19 @@ func (s *APIServer) PrepareRun() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *APIServer) installKubeSphereAPIs() {
|
func (s *APIServer) installKubeSphereAPIs() {
|
||||||
|
urlruntime.Must(v1alpha2.AddToContainer(s.container, s.Config))
|
||||||
urlruntime.Must(resourcev1alpha3.AddToContainer(s.container, s.InformerFactory))
|
urlruntime.Must(resourcev1alpha3.AddToContainer(s.container, s.InformerFactory))
|
||||||
// Need to refactor devops api registration, too much dependencies
|
// Need to refactor devops api registration, too much dependencies
|
||||||
urlruntime.Must(devopsv1alpha2.AddToContainer(s.container, s.DevopsClient, s.DBClient.Database(), nil, s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory(), s.S3Client))
|
//urlruntime.Must(devopsv1alpha2.AddToContainer(s.container, s.DevopsClient, s.DBClient.Database(), nil, s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory(), s.S3Client))
|
||||||
urlruntime.Must(loggingv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.LoggingClient))
|
urlruntime.Must(loggingv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.LoggingClient))
|
||||||
urlruntime.Must(monitoringv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.MonitoringClient))
|
urlruntime.Must(monitoringv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.MonitoringClient))
|
||||||
urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient))
|
urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient))
|
||||||
urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes()))
|
urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes()))
|
||||||
urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory))
|
urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory))
|
||||||
urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.DBClient.Database()))
|
//urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.DBClient.Database()))
|
||||||
urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.KubernetesClient.Config()))
|
urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.KubernetesClient.Config()))
|
||||||
urlruntime.Must(iamv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.LdapClient, s.CacheClient, s.AuthenticateOptions))
|
urlruntime.Must(iamv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.LdapClient, s.CacheClient, s.Config.AuthenticationOptions))
|
||||||
urlruntime.Must(oauth.AddToContainer(s.container, token.NewJwtTokenIssuer(token.DefaultIssuerName, s.AuthenticateOptions, s.CacheClient), &oauth2.SimpleConfigManager{}))
|
urlruntime.Must(oauth.AddToContainer(s.container, token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient), s.Config.AuthenticationOptions))
|
||||||
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container))
|
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +185,7 @@ func (s *APIServer) buildHandlerChain() {
|
|||||||
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
|
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
|
||||||
handler = filters.WithMultipleClusterDispatcher(handler, dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Tower().V1alpha1().Agents().Lister()))
|
handler = filters.WithMultipleClusterDispatcher(handler, dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Tower().V1alpha1().Agents().Lister()))
|
||||||
|
|
||||||
excludedPaths := []string{"/oauth/authorize", "/oauth/token"}
|
excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"}
|
||||||
pathAuthorizer, _ := path.NewAuthorizer(excludedPaths)
|
pathAuthorizer, _ := path.NewAuthorizer(excludedPaths)
|
||||||
authorizer := unionauthorizer.New(pathAuthorizer,
|
authorizer := unionauthorizer.New(pathAuthorizer,
|
||||||
authorizerfactory.NewOPAAuthorizer(am.NewFakeAMOperator()))
|
authorizerfactory.NewOPAAuthorizer(am.NewFakeAMOperator()))
|
||||||
@@ -196,7 +194,7 @@ func (s *APIServer) buildHandlerChain() {
|
|||||||
authn := unionauth.New(anonymous.NewAuthenticator(),
|
authn := unionauth.New(anonymous.NewAuthenticator(),
|
||||||
basictoken.New(basic.NewBasicAuthenticator(im.NewFakeOperator())),
|
basictoken.New(basic.NewBasicAuthenticator(im.NewFakeOperator())),
|
||||||
bearertoken.New(jwttoken.NewTokenAuthenticator(
|
bearertoken.New(jwttoken.NewTokenAuthenticator(
|
||||||
token.NewJwtTokenIssuer(token.DefaultIssuerName, s.AuthenticateOptions, s.CacheClient))))
|
token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient))))
|
||||||
handler = filters.WithAuthentication(handler, authn)
|
handler = filters.WithAuthentication(handler, authn)
|
||||||
handler = filters.WithRequestInfo(handler, requestInfoResolver)
|
handler = filters.WithRequestInfo(handler, requestInfoResolver)
|
||||||
s.Server.Handler = handler
|
s.Server.Handler = handler
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func NewTokenAuthenticator(issuer token2.Issuer) authenticator.Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *tokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
func (t *tokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
||||||
providedUser, _, err := t.jwtTokenIssuer.Verify(token)
|
providedUser, err := t.jwtTokenIssuer.Verify(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 identityprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorIdentityProviderNotFound = errors.New("the identity provider was not found")
|
||||||
|
ErrorAlreadyRegistered = errors.New("the identity provider was not found")
|
||||||
|
oauthProviderCodecs = map[string]OAuthProviderCodec{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type OAuthProvider interface {
|
||||||
|
IdentityExchange(code string) (user.Info, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type OAuthProviderCodec interface {
|
||||||
|
Type() string
|
||||||
|
Decode(options *oauth.DynamicOptions) (OAuthProvider, error)
|
||||||
|
Encode(provider OAuthProvider) (*oauth.DynamicOptions, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveOAuthProvider(providerType string, options *oauth.DynamicOptions) (OAuthProvider, error) {
|
||||||
|
if codec, ok := oauthProviderCodecs[providerType]; ok {
|
||||||
|
return codec.Decode(options)
|
||||||
|
}
|
||||||
|
return nil, ErrorIdentityProviderNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveOAuthOptions(providerType string, provider OAuthProvider) (*oauth.DynamicOptions, error) {
|
||||||
|
if codec, ok := oauthProviderCodecs[providerType]; ok {
|
||||||
|
return codec.Encode(provider)
|
||||||
|
}
|
||||||
|
return nil, ErrorIdentityProviderNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterOAuthProviderCodec(codec OAuthProviderCodec) error {
|
||||||
|
if _, ok := oauthProviderCodecs[codec.Type()]; ok {
|
||||||
|
return ErrorAlreadyRegistered
|
||||||
|
}
|
||||||
|
oauthProviderCodecs[codec.Type()] = codec
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* 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 oauth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ConfigNotFound = errors.New("config not found")
|
|
||||||
|
|
||||||
type Configuration interface {
|
|
||||||
Load(clientId string) (*oauth2.Config, error)
|
|
||||||
}
|
|
||||||
235
pkg/apiserver/authentication/oauth/oauth_options.go
Normal file
235
pkg/apiserver/authentication/oauth/oauth_options.go
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 oauth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GrantHandlerType string
|
||||||
|
type MappingMethod string
|
||||||
|
type IdentityProviderType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GrantHandlerAuto auto-approves client authorization grant requests
|
||||||
|
GrantHandlerAuto GrantHandlerType = "auto"
|
||||||
|
// GrantHandlerPrompt prompts the user to approve new client authorization grant requests
|
||||||
|
GrantHandlerPrompt GrantHandlerType = "prompt"
|
||||||
|
// GrantHandlerDeny auto-denies client authorization grant requests
|
||||||
|
GrantHandlerDeny GrantHandlerType = "deny"
|
||||||
|
// MappingMethodAuto The default value.The user will automatically create and mapping when login successful.
|
||||||
|
// Fails if a user with that user name is already mapped to another identity.
|
||||||
|
MappingMethodAuto MappingMethod = "auto"
|
||||||
|
// MappingMethodLookup Looks up an existing identity, user identity mapping, and user, but does not automatically
|
||||||
|
// provision users or identities. Using this method requires you to manually provision users.
|
||||||
|
MappingMethodLookup MappingMethod = "lookup"
|
||||||
|
// MappingMethodMixed A user entity can be mapped with multiple identifyProvider.
|
||||||
|
MappingMethodMixed MappingMethod = "mixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorClientNotFound = errors.New("the OAuth client was not found")
|
||||||
|
ErrorRedirectURLNotAllowed = errors.New("redirect URL is not allowed")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
// LDAPPasswordIdentityProvider provider is used by default.
|
||||||
|
IdentityProviders []IdentityProviderOptions `json:"identityProviders,omitempty" yaml:"identityProviders,omitempty"`
|
||||||
|
|
||||||
|
// Register additional OAuth clients.
|
||||||
|
Clients []Client `json:"clients,omitempty" yaml:"clients,omitempty"`
|
||||||
|
|
||||||
|
// AccessTokenMaxAgeSeconds control the lifetime of access tokens. The default lifetime is 24 hours.
|
||||||
|
// 0 means no expiration.
|
||||||
|
AccessTokenMaxAge time.Duration `json:"accessTokenMaxAge" yaml:"accessTokenMaxAge"`
|
||||||
|
|
||||||
|
// Inactivity timeout for tokens
|
||||||
|
// The value represents the maximum amount of time that can occur between
|
||||||
|
// consecutive uses of the token. Tokens become invalid if they are not
|
||||||
|
// used within this temporal window. The user will need to acquire a new
|
||||||
|
// token to regain access once a token times out.
|
||||||
|
// This value needs to be set only if the default set in configuration is
|
||||||
|
// not appropriate for this client. Valid values are:
|
||||||
|
// - 0: Tokens for this client never time out
|
||||||
|
// - X: Tokens time out if there is no activity
|
||||||
|
// The current minimum allowed value for X is 5 minutes
|
||||||
|
AccessTokenInactivityTimeout time.Duration `json:"accessTokenInactivityTimeout" yaml:"accessTokenInactivityTimeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynamicOptions map[string]interface{}
|
||||||
|
|
||||||
|
type IdentityProviderOptions struct {
|
||||||
|
// The provider name.
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
|
||||||
|
// Defines how new identities are mapped to users when they login. Allowed values are:
|
||||||
|
// - auto: The default value.The user will automatically create and mapping when login successful.
|
||||||
|
// Fails if a user with that user name is already mapped to another identity.
|
||||||
|
// - lookup: Looks up an existing identity, user identity mapping, and user, but does not automatically
|
||||||
|
// provision users or identities. Using this method requires you to manually provision users.
|
||||||
|
// - mixed: A user entity can be mapped with multiple identifyProvider.
|
||||||
|
MappingMethod MappingMethod `json:"mappingMethod" yaml:"mappingMethod"`
|
||||||
|
|
||||||
|
// When true, unauthenticated token requests from web clients (like the web console)
|
||||||
|
// are redirected to a login page (with WWW-Authenticate challenge header) backed by this provider.
|
||||||
|
LoginRedirect bool `json:"loginRedirect" yaml:"loginRedirect"`
|
||||||
|
|
||||||
|
// The type of identify provider
|
||||||
|
Type string `json:"type" yaml:"type"`
|
||||||
|
|
||||||
|
// The options of identify provider
|
||||||
|
Provider *DynamicOptions `json:"provider,omitempty" yaml:"provider,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
// AccessToken is the token that authorizes and authenticates
|
||||||
|
// the requests.
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
|
||||||
|
// TokenType is the type of token.
|
||||||
|
// The Type method returns either this or "Bearer", the default.
|
||||||
|
TokenType string `json:"token_type,omitempty"`
|
||||||
|
|
||||||
|
// RefreshToken is a token that's used by the application
|
||||||
|
// (as opposed to the user) to refresh the access token
|
||||||
|
// if it expires.
|
||||||
|
RefreshToken string `json:"refresh_token,omitempty"`
|
||||||
|
|
||||||
|
// ExpiresIn is the optional expiration second of the access token.
|
||||||
|
ExpiresIn int `json:"expires_in,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
// The name of the OAuth client is used as the client_id parameter when making requests to <master>/oauth/authorize
|
||||||
|
// and <master>/oauth/token.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Secret is the unique secret associated with a client
|
||||||
|
Secret string `json:"-" yaml:"secret,omitempty"`
|
||||||
|
|
||||||
|
// RespondWithChallenges indicates whether the client wants authentication needed responses made
|
||||||
|
// in the form of challenges instead of redirects
|
||||||
|
RespondWithChallenges bool `json:"respondWithChallenges,omitempty" yaml:"respondWithChallenges,omitempty"`
|
||||||
|
|
||||||
|
// RedirectURIs is the valid redirection URIs associated with a client
|
||||||
|
RedirectURIs []string `json:"redirectURIs,omitempty" yaml:"redirectURIs,omitempty"`
|
||||||
|
|
||||||
|
// GrantMethod determines how to handle grants for this client. If no method is provided, the
|
||||||
|
// cluster default grant handling method will be used. Valid grant handling methods are:
|
||||||
|
// - auto: always approves grant requests, useful for trusted clients
|
||||||
|
// - prompt: prompts the end user for approval of grant requests, useful for third-party clients
|
||||||
|
// - deny: always denies grant requests, useful for black-listed clients
|
||||||
|
GrantMethod GrantHandlerType `json:"grantMethod,omitempty" yaml:"grantMethod,omitempty"`
|
||||||
|
|
||||||
|
// ScopeRestrictions describes which scopes this client can request. Each requested scope
|
||||||
|
// is checked against each restriction. If any restriction matches, then the scope is allowed.
|
||||||
|
// If no restriction matches, then the scope is denied.
|
||||||
|
ScopeRestrictions []string `json:"scopeRestrictions,omitempty" yaml:"scopeRestrictions,omitempty"`
|
||||||
|
|
||||||
|
// AccessTokenMaxAge overrides the default access token max age for tokens granted to this client.
|
||||||
|
AccessTokenMaxAge *time.Duration `json:"accessTokenMaxAge,omitempty" yaml:"accessTokenMaxAge,omitempty"`
|
||||||
|
|
||||||
|
// AccessTokenInactivityTimeout overrides the default token
|
||||||
|
// inactivity timeout for tokens granted to this client.
|
||||||
|
AccessTokenInactivityTimeout *time.Duration `json:"accessTokenInactivityTimeout,omitempty" yaml:"accessTokenInactivityTimeout,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Allow any redirect URI if the redirectURI is defined in request
|
||||||
|
AllowAllRedirectURI = "*"
|
||||||
|
DefaultTokenMaxAge = time.Second * 86400
|
||||||
|
DefaultAccessTokenInactivityTimeout = time.Duration(0)
|
||||||
|
DefaultClients = []Client{{
|
||||||
|
Name: "default",
|
||||||
|
RespondWithChallenges: true,
|
||||||
|
RedirectURIs: []string{AllowAllRedirectURI},
|
||||||
|
GrantMethod: GrantHandlerAuto,
|
||||||
|
ScopeRestrictions: []string{"full"},
|
||||||
|
AccessTokenMaxAge: &DefaultTokenMaxAge,
|
||||||
|
AccessTokenInactivityTimeout: &DefaultAccessTokenInactivityTimeout,
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (o *Options) OAuthClient(name string) (Client, error) {
|
||||||
|
for _, found := range o.Clients {
|
||||||
|
if found.Name == name {
|
||||||
|
return found, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, defaultClient := range DefaultClients {
|
||||||
|
if defaultClient.Name == name {
|
||||||
|
return defaultClient, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Client{}, ErrorClientNotFound
|
||||||
|
}
|
||||||
|
func (o *Options) IdentityProviderOptions(name string) (IdentityProviderOptions, error) {
|
||||||
|
for _, found := range o.IdentityProviders {
|
||||||
|
if found.Name == name {
|
||||||
|
return found, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IdentityProviderOptions{}, ErrorClientNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) anyRedirectAbleURI() []string {
|
||||||
|
uris := make([]string, 0)
|
||||||
|
for _, uri := range c.RedirectURIs {
|
||||||
|
_, err := url.Parse(uri)
|
||||||
|
if err == nil {
|
||||||
|
uris = append(uris, uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uris
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) ResolveRedirectURL(expectURL string) (string, error) {
|
||||||
|
// RedirectURIs is empty
|
||||||
|
if len(c.RedirectURIs) == 0 {
|
||||||
|
return "", ErrorRedirectURLNotAllowed
|
||||||
|
}
|
||||||
|
allowAllRedirectURI := sliceutil.HasString(c.RedirectURIs, AllowAllRedirectURI)
|
||||||
|
redirectAbleURIs := c.anyRedirectAbleURI()
|
||||||
|
|
||||||
|
if expectURL == "" {
|
||||||
|
// Need to specify at least one RedirectURI
|
||||||
|
if len(redirectAbleURIs) > 0 {
|
||||||
|
return redirectAbleURIs[0], nil
|
||||||
|
} else {
|
||||||
|
return "", ErrorRedirectURLNotAllowed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allowAllRedirectURI || sliceutil.HasString(redirectAbleURIs, expectURL) {
|
||||||
|
return expectURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrorRedirectURLNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOptions() *Options {
|
||||||
|
return &Options{
|
||||||
|
IdentityProviders: make([]IdentityProviderOptions, 0),
|
||||||
|
Clients: make([]Client, 0),
|
||||||
|
AccessTokenMaxAge: time.Hour * 24,
|
||||||
|
AccessTokenInactivityTimeout: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
104
pkg/apiserver/authentication/oauth/oauth_options_test.go
Normal file
104
pkg/apiserver/authentication/oauth/oauth_options_test.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 oauth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultAuthOptions(t *testing.T) {
|
||||||
|
oneDay := time.Second * 86400
|
||||||
|
zero := time.Duration(0)
|
||||||
|
expect := Client{
|
||||||
|
Name: "default",
|
||||||
|
RespondWithChallenges: true,
|
||||||
|
RedirectURIs: []string{AllowAllRedirectURI},
|
||||||
|
GrantMethod: GrantHandlerAuto,
|
||||||
|
ScopeRestrictions: []string{"full"},
|
||||||
|
AccessTokenMaxAge: &oneDay,
|
||||||
|
AccessTokenInactivityTimeout: &zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
options := NewOptions()
|
||||||
|
client, err := options.OAuthClient("default")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(expect, client); len(diff) != 0 {
|
||||||
|
t.Errorf("%T differ (-got, +expected), %s", expect, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientResolveRedirectURL(t *testing.T) {
|
||||||
|
|
||||||
|
options := NewOptions()
|
||||||
|
defaultClient, err := options.OAuthClient("default")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
Name string
|
||||||
|
client Client
|
||||||
|
expectError error
|
||||||
|
expectURL string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "default client test",
|
||||||
|
client: defaultClient,
|
||||||
|
expectError: nil,
|
||||||
|
expectURL: "https://localhost:8080/auth/cb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "custom client test",
|
||||||
|
client: Client{
|
||||||
|
Name: "default",
|
||||||
|
RespondWithChallenges: true,
|
||||||
|
RedirectURIs: []string{"https://foo.bar.com/oauth/cb"},
|
||||||
|
GrantMethod: GrantHandlerAuto,
|
||||||
|
ScopeRestrictions: []string{"full"},
|
||||||
|
},
|
||||||
|
expectError: ErrorRedirectURLNotAllowed,
|
||||||
|
expectURL: "https://foo.bar.com/oauth/err",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "custom client test",
|
||||||
|
client: Client{
|
||||||
|
Name: "default",
|
||||||
|
RespondWithChallenges: true,
|
||||||
|
RedirectURIs: []string{AllowAllRedirectURI, "https://foo.bar.com/oauth/cb"},
|
||||||
|
GrantMethod: GrantHandlerAuto,
|
||||||
|
ScopeRestrictions: []string{"full"},
|
||||||
|
},
|
||||||
|
expectError: nil,
|
||||||
|
expectURL: "https://foo.bar.com/oauth/err2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
redirectURL, err := test.client.ResolveRedirectURL(test.expectURL)
|
||||||
|
if err != test.expectError {
|
||||||
|
t.Errorf("expected error: %s, got: %s", test.expectError, err)
|
||||||
|
}
|
||||||
|
if test.expectError == nil && test.expectURL != redirectURL {
|
||||||
|
t.Errorf("expected redirect url: %s, got: %s", test.expectURL, redirectURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* 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 oauth
|
|
||||||
|
|
||||||
import "golang.org/x/oauth2"
|
|
||||||
|
|
||||||
type SimpleConfigManager struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SimpleConfigManager) Load(clientId string) (*oauth2.Config, error) {
|
|
||||||
if clientId == "kubesphere-console-client" {
|
|
||||||
return &oauth2.Config{
|
|
||||||
ClientID: "8b21fef43889a28f2bd6",
|
|
||||||
ClientSecret: "xb21fef43889a28f2bd6",
|
|
||||||
Endpoint: oauth2.Endpoint{AuthURL: "http://ks-apiserver.kubesphere-system.svc/oauth/authorize", TokenURL: "http://ks-apiserver.kubesphere.io/oauth/token"},
|
|
||||||
RedirectURL: "http://ks-console.kubesphere-system.svc/oauth/token/implicit",
|
|
||||||
Scopes: nil,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, ConfigNotFound
|
|
||||||
}
|
|
||||||
@@ -16,11 +16,12 @@
|
|||||||
* /
|
* /
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package auth
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,15 +33,13 @@ type AuthenticationOptions struct {
|
|||||||
// maximum retries when authenticate failed
|
// maximum retries when authenticate failed
|
||||||
MaxAuthenticateRetries int `json:"maxAuthenticateRetries" yaml:"maxAuthenticateRetries"`
|
MaxAuthenticateRetries int `json:"maxAuthenticateRetries" yaml:"maxAuthenticateRetries"`
|
||||||
|
|
||||||
// token validation duration, will refresh token expiration for each user request
|
|
||||||
// 0 means never expire
|
|
||||||
TokenExpiration time.Duration `json:"tokenExpiration" yaml:"tokenExpiration"`
|
|
||||||
|
|
||||||
// allow multiple users login at the same time
|
// allow multiple users login at the same time
|
||||||
MultipleLogin bool `json:"multipleLogin" yaml:"multipleLogin"`
|
MultipleLogin bool `json:"multipleLogin" yaml:"multipleLogin"`
|
||||||
|
|
||||||
// secret to signed jwt token
|
// secret to signed jwt token
|
||||||
JwtSecret string `json:"jwtSecret" yaml:"jwtSecret"`
|
JwtSecret string `json:"-" yaml:"jwtSecret"`
|
||||||
|
|
||||||
|
OAuthOptions *oauth.Options `json:"oauthOptions" yaml:"oauthOptions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthenticateOptions() *AuthenticationOptions {
|
func NewAuthenticateOptions() *AuthenticationOptions {
|
||||||
@@ -48,7 +47,7 @@ func NewAuthenticateOptions() *AuthenticationOptions {
|
|||||||
AuthenticateRateLimiterMaxTries: 5,
|
AuthenticateRateLimiterMaxTries: 5,
|
||||||
AuthenticateRateLimiterDuration: time.Minute * 30,
|
AuthenticateRateLimiterDuration: time.Minute * 30,
|
||||||
MaxAuthenticateRetries: 0,
|
MaxAuthenticateRetries: 0,
|
||||||
TokenExpiration: 0,
|
OAuthOptions: oauth.NewOptions(),
|
||||||
MultipleLogin: false,
|
MultipleLogin: false,
|
||||||
JwtSecret: "",
|
JwtSecret: "",
|
||||||
}
|
}
|
||||||
@@ -68,7 +67,6 @@ func (options *AuthenticationOptions) AddFlags(fs *pflag.FlagSet, s *Authenticat
|
|||||||
fs.IntVar(&options.AuthenticateRateLimiterMaxTries, "authenticate-rate-limiter-max-retries", s.AuthenticateRateLimiterMaxTries, "")
|
fs.IntVar(&options.AuthenticateRateLimiterMaxTries, "authenticate-rate-limiter-max-retries", s.AuthenticateRateLimiterMaxTries, "")
|
||||||
fs.DurationVar(&options.AuthenticateRateLimiterDuration, "authenticate-rate-limiter-duration", s.AuthenticateRateLimiterDuration, "")
|
fs.DurationVar(&options.AuthenticateRateLimiterDuration, "authenticate-rate-limiter-duration", s.AuthenticateRateLimiterDuration, "")
|
||||||
fs.IntVar(&options.MaxAuthenticateRetries, "authenticate-max-retries", s.MaxAuthenticateRetries, "")
|
fs.IntVar(&options.MaxAuthenticateRetries, "authenticate-max-retries", s.MaxAuthenticateRetries, "")
|
||||||
fs.DurationVar(&options.TokenExpiration, "token-expiration", s.TokenExpiration, "Token expire duration, for example 30m/2h/1d, 0 means token never expire unless server restart.")
|
|
||||||
fs.BoolVar(&options.MultipleLogin, "multiple-login", s.MultipleLogin, "Allow multiple login with the same account, disable means only one user can login at the same time.")
|
fs.BoolVar(&options.MultipleLogin, "multiple-login", s.MultipleLogin, "Allow multiple login with the same account, disable means only one user can login at the same time.")
|
||||||
fs.StringVar(&options.JwtSecret, "jwt-secret", s.JwtSecret, "Secret to sign jwt token, must not be empty.")
|
fs.StringVar(&options.JwtSecret, "jwt-secret", s.JwtSecret, "Secret to sign jwt token, must not be empty.")
|
||||||
}
|
}
|
||||||
@@ -18,13 +18,15 @@
|
|||||||
|
|
||||||
package token
|
package token
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
// Issuer issues token to user, tokens are required to perform mutating requests to resources
|
// Issuer issues token to user, tokens are required to perform mutating requests to resources
|
||||||
type Issuer interface {
|
type Issuer interface {
|
||||||
// IssueTo issues a token a User, return error if issuing process failed
|
// IssueTo issues a token a User, return error if issuing process failed
|
||||||
IssueTo(User) (string, *Claims, error)
|
IssueTo(user User, expiresIn time.Duration) (string, error)
|
||||||
|
|
||||||
// Verify verifies a token, and return a User if it's a valid token, otherwise return error
|
// Verify verifies a token, and return a User if it's a valid token, otherwise return error
|
||||||
Verify(string) (User, *Claims, error)
|
Verify(string) (User, error)
|
||||||
|
|
||||||
// Revoke a token,
|
// Revoke a token,
|
||||||
Revoke(token string) error
|
Revoke(token string) error
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ package token
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
|
||||||
"kubesphere.io/kubesphere/pkg/api/iam"
|
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||||
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||||
"time"
|
"time"
|
||||||
@@ -44,35 +44,35 @@ type Claims struct {
|
|||||||
|
|
||||||
type jwtTokenIssuer struct {
|
type jwtTokenIssuer struct {
|
||||||
name string
|
name string
|
||||||
options *auth.AuthenticationOptions
|
options *authoptions.AuthenticationOptions
|
||||||
cache cache.Interface
|
cache cache.Interface
|
||||||
keyFunc jwt.Keyfunc
|
keyFunc jwt.Keyfunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *jwtTokenIssuer) Verify(tokenString string) (User, *Claims, error) {
|
func (s *jwtTokenIssuer) Verify(tokenString string) (User, error) {
|
||||||
if len(tokenString) == 0 {
|
if len(tokenString) == 0 {
|
||||||
return nil, nil, errInvalidToken
|
return nil, errInvalidToken
|
||||||
}
|
}
|
||||||
_, err := s.cache.Get(tokenCacheKey(tokenString))
|
_, err := s.cache.Get(tokenCacheKey(tokenString))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == cache.ErrNoSuchKey {
|
if err == cache.ErrNoSuchKey {
|
||||||
return nil, nil, errTokenExpired
|
return nil, errTokenExpired
|
||||||
}
|
}
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
clm := &Claims{}
|
clm := &Claims{}
|
||||||
|
|
||||||
_, err = jwt.ParseWithClaims(tokenString, clm, s.keyFunc)
|
_, err = jwt.ParseWithClaims(tokenString, clm, s.keyFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &iam.User{Name: clm.Username, UID: clm.UID}, clm, nil
|
return &iam.User{Name: clm.Username, UID: clm.UID}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *jwtTokenIssuer) IssueTo(user User) (string, *Claims, error) {
|
func (s *jwtTokenIssuer) IssueTo(user User, expiresIn time.Duration) (string, error) {
|
||||||
clm := &Claims{
|
clm := &Claims{
|
||||||
Username: user.GetName(),
|
Username: user.GetName(),
|
||||||
UID: user.GetUID(),
|
UID: user.GetUID(),
|
||||||
@@ -83,8 +83,8 @@ func (s *jwtTokenIssuer) IssueTo(user User) (string, *Claims, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.options.TokenExpiration > 0 {
|
if expiresIn > 0 {
|
||||||
clm.ExpiresAt = clm.IssuedAt + int64(s.options.TokenExpiration.Seconds())
|
clm.ExpiresAt = clm.IssuedAt + int64(expiresIn.Seconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, clm)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, clm)
|
||||||
@@ -92,19 +92,19 @@ func (s *jwtTokenIssuer) IssueTo(user User) (string, *Claims, error) {
|
|||||||
tokenString, err := token.SignedString([]byte(s.options.JwtSecret))
|
tokenString, err := token.SignedString([]byte(s.options.JwtSecret))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.cache.Set(tokenCacheKey(tokenString), tokenString, s.options.TokenExpiration)
|
s.cache.Set(tokenCacheKey(tokenString), tokenString, expiresIn)
|
||||||
|
|
||||||
return tokenString, clm, nil
|
return tokenString, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *jwtTokenIssuer) Revoke(token string) error {
|
func (s *jwtTokenIssuer) Revoke(token string) error {
|
||||||
return s.cache.Del(tokenCacheKey(token))
|
return s.cache.Del(tokenCacheKey(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJwtTokenIssuer(issuerName string, options *auth.AuthenticationOptions, cache cache.Interface) Issuer {
|
func NewJwtTokenIssuer(issuerName string, options *authoptions.AuthenticationOptions, cache cache.Interface) Issuer {
|
||||||
return &jwtTokenIssuer{
|
return &jwtTokenIssuer{
|
||||||
name: issuerName,
|
name: issuerName,
|
||||||
options: options,
|
options: options,
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ package token
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
|
||||||
"kubesphere.io/kubesphere/pkg/api/iam"
|
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||||
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestJwtTokenIssuer(t *testing.T) {
|
func TestJwtTokenIssuer(t *testing.T) {
|
||||||
options := auth.NewAuthenticateOptions()
|
options := authoptions.NewAuthenticateOptions()
|
||||||
options.JwtSecret = "kubesphere"
|
options.JwtSecret = "kubesphere"
|
||||||
issuer := NewJwtTokenIssuer(DefaultIssuerName, options, cache.NewSimpleCache())
|
issuer := NewJwtTokenIssuer(DefaultIssuerName, options, cache.NewSimpleCache())
|
||||||
|
|
||||||
@@ -54,12 +54,12 @@ func TestJwtTokenIssuer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
token, _, err := issuer.IssueTo(user)
|
token, err := issuer.IssueTo(user, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, _, err := issuer.Verify(token)
|
got, err := issuer.Verify(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import (
|
|||||||
"github.com/emicklei/go-restful"
|
"github.com/emicklei/go-restful"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/alerting"
|
"kubesphere.io/kubesphere/pkg/simple/client/alerting"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
|
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
|
"kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
|
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
|
||||||
@@ -74,12 +73,7 @@ type Config struct {
|
|||||||
OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"`
|
OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"`
|
||||||
MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"`
|
MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"`
|
||||||
LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"`
|
LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"`
|
||||||
|
AuthenticationOptions *authoptions.AuthenticationOptions `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"`
|
||||||
// Options below are only loaded from configuration file, no command line flags for these options now.
|
|
||||||
KubeSphereOptions *kubesphere.Options `json:"-" yaml:"kubesphere,omitempty" mapstructure:"kubesphere"`
|
|
||||||
|
|
||||||
AuthenticateOptions *auth.AuthenticationOptions `json:"authentication,omitempty" yaml:"authenticate,omitempty" mapstructure:"authenticate"`
|
|
||||||
|
|
||||||
// Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere,
|
// Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere,
|
||||||
// we can add these options to kubesphere command lines
|
// we can add these options to kubesphere command lines
|
||||||
AlertingOptions *alerting.Options `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"`
|
AlertingOptions *alerting.Options `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"`
|
||||||
@@ -99,11 +93,10 @@ func New() *Config {
|
|||||||
S3Options: s3.NewS3Options(),
|
S3Options: s3.NewS3Options(),
|
||||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||||
MonitoringOptions: prometheus.NewPrometheusOptions(),
|
MonitoringOptions: prometheus.NewPrometheusOptions(),
|
||||||
KubeSphereOptions: kubesphere.NewKubeSphereOptions(),
|
|
||||||
AlertingOptions: alerting.NewAlertingOptions(),
|
AlertingOptions: alerting.NewAlertingOptions(),
|
||||||
NotificationOptions: notification.NewNotificationOptions(),
|
NotificationOptions: notification.NewNotificationOptions(),
|
||||||
LoggingOptions: elasticsearch.NewElasticSearchOptions(),
|
LoggingOptions: elasticsearch.NewElasticSearchOptions(),
|
||||||
AuthenticateOptions: auth.NewAuthenticateOptions(),
|
AuthenticationOptions: authoptions.NewAuthenticateOptions(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,17 +118,9 @@ func TryLoadFromDisk() (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conf := New()
|
conf := New()
|
||||||
|
|
||||||
if err := viper.Unmarshal(conf); err != nil {
|
if err := viper.Unmarshal(conf); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
|
||||||
// make sure kubesphere options always exists
|
|
||||||
if conf.KubeSphereOptions == nil {
|
|
||||||
conf.KubeSphereOptions = kubesphere.NewKubeSphereOptions()
|
|
||||||
} else {
|
|
||||||
ksOptions := kubesphere.NewKubeSphereOptions()
|
|
||||||
conf.KubeSphereOptions.ApplyTo(ksOptions)
|
|
||||||
conf.KubeSphereOptions = ksOptions
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
@@ -151,7 +136,7 @@ func (conf *Config) InstallAPI(c *restful.Container) {
|
|||||||
ws.Route(ws.GET("/configz").
|
ws.Route(ws.GET("/configz").
|
||||||
To(func(request *restful.Request, response *restful.Response) {
|
To(func(request *restful.Request, response *restful.Response) {
|
||||||
conf.stripEmptyOptions()
|
conf.stripEmptyOptions()
|
||||||
response.WriteAsJson(conf.toMap())
|
response.WriteAsJson(conf.ToMap())
|
||||||
}).
|
}).
|
||||||
Doc("Get system components configuration").
|
Doc("Get system components configuration").
|
||||||
Produces(restful.MIME_JSON).
|
Produces(restful.MIME_JSON).
|
||||||
@@ -163,7 +148,8 @@ func (conf *Config) InstallAPI(c *restful.Container) {
|
|||||||
|
|
||||||
// convertToMap simply converts config to map[string]bool
|
// convertToMap simply converts config to map[string]bool
|
||||||
// to hide sensitive information
|
// to hide sensitive information
|
||||||
func (conf *Config) toMap() map[string]bool {
|
func (conf *Config) ToMap() map[string]bool {
|
||||||
|
conf.stripEmptyOptions()
|
||||||
result := make(map[string]bool, 0)
|
result := make(map[string]bool, 0)
|
||||||
|
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||||
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/alerting"
|
"kubesphere.io/kubesphere/pkg/simple/client/alerting"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
|
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
|
"kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
|
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
|
||||||
@@ -19,14 +20,14 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/servicemesh"
|
"kubesphere.io/kubesphere/pkg/simple/client/servicemesh"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
|
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
|
||||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestConfig() *Config {
|
func newTestConfig() (*Config, error) {
|
||||||
conf := &Config{
|
|
||||||
|
var conf = &Config{
|
||||||
MySQLOptions: &mysql.Options{
|
MySQLOptions: &mysql.Options{
|
||||||
Host: "10.68.96.5:3306",
|
Host: "10.68.96.5:3306",
|
||||||
Username: "root",
|
Username: "root",
|
||||||
@@ -96,25 +97,34 @@ func newTestConfig() *Config {
|
|||||||
IndexPrefix: "elk",
|
IndexPrefix: "elk",
|
||||||
Version: "6",
|
Version: "6",
|
||||||
},
|
},
|
||||||
KubeSphereOptions: &kubesphere.Options{
|
|
||||||
APIServer: "http://ks-apiserver.kubesphere-system.svc",
|
|
||||||
AccountServer: "http://ks-account.kubesphere-system.svc",
|
|
||||||
},
|
|
||||||
AlertingOptions: &alerting.Options{
|
AlertingOptions: &alerting.Options{
|
||||||
Endpoint: "http://alerting.kubesphere-alerting-system.svc:9200",
|
Endpoint: "http://alerting.kubesphere-alerting-system.svc:9200",
|
||||||
},
|
},
|
||||||
NotificationOptions: ¬ification.Options{
|
NotificationOptions: ¬ification.Options{
|
||||||
Endpoint: "http://notification.kubesphere-alerting-system.svc:9200",
|
Endpoint: "http://notification.kubesphere-alerting-system.svc:9200",
|
||||||
},
|
},
|
||||||
AuthenticateOptions: &auth.AuthenticationOptions{
|
AuthenticationOptions: &authoptions.AuthenticationOptions{
|
||||||
AuthenticateRateLimiterMaxTries: 5,
|
AuthenticateRateLimiterMaxTries: 5,
|
||||||
AuthenticateRateLimiterDuration: 30 * time.Minute,
|
AuthenticateRateLimiterDuration: 30 * time.Minute,
|
||||||
MaxAuthenticateRetries: 6,
|
MaxAuthenticateRetries: 6,
|
||||||
TokenExpiration: 30 * time.Minute,
|
JwtSecret: "xxxxxx",
|
||||||
MultipleLogin: false,
|
MultipleLogin: false,
|
||||||
|
OAuthOptions: &oauth.Options{
|
||||||
|
IdentityProviders: []oauth.IdentityProviderOptions{},
|
||||||
|
Clients: []oauth.Client{{
|
||||||
|
Name: "kubesphere-console-client",
|
||||||
|
Secret: "xxxxxx-xxxxxx-xxxxxx",
|
||||||
|
RespondWithChallenges: true,
|
||||||
|
RedirectURIs: []string{"http://ks-console.kubesphere-system.svc/oauth/token/implicit"},
|
||||||
|
GrantMethod: oauth.GrantHandlerAuto,
|
||||||
|
AccessTokenInactivityTimeout: nil,
|
||||||
|
}},
|
||||||
|
AccessTokenMaxAge: time.Hour * 24,
|
||||||
|
AccessTokenInactivityTimeout: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return conf
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveTestConfig(t *testing.T, conf *Config) {
|
func saveTestConfig(t *testing.T, conf *Config) {
|
||||||
@@ -122,7 +132,6 @@ func saveTestConfig(t *testing.T, conf *Config) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error marshal config. %v", err)
|
t.Fatalf("error marshal config. %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(fmt.Sprintf("%s.yaml", defaultConfigurationName), content, 0640)
|
err = ioutil.WriteFile(fmt.Sprintf("%s.yaml", defaultConfigurationName), content, 0640)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error write configuration file, %v", err)
|
t.Fatalf("error write configuration file, %v", err)
|
||||||
@@ -144,7 +153,10 @@ func cleanTestConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
conf := newTestConfig()
|
conf, err := newTestConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
saveTestConfig(t, conf)
|
saveTestConfig(t, conf)
|
||||||
defer cleanTestConfig(t)
|
defer cleanTestConfig(t)
|
||||||
|
|
||||||
@@ -152,48 +164,7 @@ func TestGet(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if diff := cmp.Diff(conf, conf2); diff != "" {
|
||||||
if diff := reflectutils.Equal(conf, conf2); diff != nil {
|
|
||||||
t.Fatal(diff)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKubeSphereOptions(t *testing.T) {
|
|
||||||
conf := newTestConfig()
|
|
||||||
|
|
||||||
t.Run("save nil kubesphere options", func(t *testing.T) {
|
|
||||||
savedConf := *conf
|
|
||||||
savedConf.KubeSphereOptions = nil
|
|
||||||
saveTestConfig(t, &savedConf)
|
|
||||||
defer cleanTestConfig(t)
|
|
||||||
|
|
||||||
loadedConf, err := TryLoadFromDisk()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := reflectutils.Equal(conf, loadedConf); diff != nil {
|
|
||||||
t.Fatal(diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("save partially kubesphere options", func(t *testing.T) {
|
|
||||||
savedConf := *conf
|
|
||||||
savedConf.KubeSphereOptions.APIServer = "http://example.com"
|
|
||||||
savedConf.KubeSphereOptions.AccountServer = ""
|
|
||||||
|
|
||||||
saveTestConfig(t, &savedConf)
|
|
||||||
defer cleanTestConfig(t)
|
|
||||||
|
|
||||||
loadedConf, err := TryLoadFromDisk()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
savedConf.KubeSphereOptions.AccountServer = "http://ks-account.kubesphere-system.svc"
|
|
||||||
|
|
||||||
if diff := reflectutils.Equal(&savedConf, loadedConf); diff != nil {
|
|
||||||
t.Fatal(diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
flowcontrol "k8s.io/client-go/util/flowcontrol"
|
flowcontrol "k8s.io/client-go/util/flowcontrol"
|
||||||
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1"
|
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1"
|
||||||
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3"
|
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2"
|
||||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1"
|
networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1"
|
||||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
|
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
|
||||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1"
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1"
|
||||||
@@ -36,6 +37,7 @@ type Interface interface {
|
|||||||
Discovery() discovery.DiscoveryInterface
|
Discovery() discovery.DiscoveryInterface
|
||||||
DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface
|
DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface
|
||||||
DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface
|
DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface
|
||||||
|
IamV1alpha2() iamv1alpha2.IamV1alpha2Interface
|
||||||
NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface
|
NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface
|
||||||
ServicemeshV1alpha2() servicemeshv1alpha2.ServicemeshV1alpha2Interface
|
ServicemeshV1alpha2() servicemeshv1alpha2.ServicemeshV1alpha2Interface
|
||||||
TenantV1alpha1() tenantv1alpha1.TenantV1alpha1Interface
|
TenantV1alpha1() tenantv1alpha1.TenantV1alpha1Interface
|
||||||
@@ -48,6 +50,7 @@ type Clientset struct {
|
|||||||
*discovery.DiscoveryClient
|
*discovery.DiscoveryClient
|
||||||
devopsV1alpha1 *devopsv1alpha1.DevopsV1alpha1Client
|
devopsV1alpha1 *devopsv1alpha1.DevopsV1alpha1Client
|
||||||
devopsV1alpha3 *devopsv1alpha3.DevopsV1alpha3Client
|
devopsV1alpha3 *devopsv1alpha3.DevopsV1alpha3Client
|
||||||
|
iamV1alpha2 *iamv1alpha2.IamV1alpha2Client
|
||||||
networkV1alpha1 *networkv1alpha1.NetworkV1alpha1Client
|
networkV1alpha1 *networkv1alpha1.NetworkV1alpha1Client
|
||||||
servicemeshV1alpha2 *servicemeshv1alpha2.ServicemeshV1alpha2Client
|
servicemeshV1alpha2 *servicemeshv1alpha2.ServicemeshV1alpha2Client
|
||||||
tenantV1alpha1 *tenantv1alpha1.TenantV1alpha1Client
|
tenantV1alpha1 *tenantv1alpha1.TenantV1alpha1Client
|
||||||
@@ -64,6 +67,11 @@ func (c *Clientset) DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface {
|
|||||||
return c.devopsV1alpha3
|
return c.devopsV1alpha3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IamV1alpha2 retrieves the IamV1alpha2Client
|
||||||
|
func (c *Clientset) IamV1alpha2() iamv1alpha2.IamV1alpha2Interface {
|
||||||
|
return c.iamV1alpha2
|
||||||
|
}
|
||||||
|
|
||||||
// NetworkV1alpha1 retrieves the NetworkV1alpha1Client
|
// NetworkV1alpha1 retrieves the NetworkV1alpha1Client
|
||||||
func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface {
|
func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface {
|
||||||
return c.networkV1alpha1
|
return c.networkV1alpha1
|
||||||
@@ -113,6 +121,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
cs.iamV1alpha2, err = iamv1alpha2.NewForConfig(&configShallowCopy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
cs.networkV1alpha1, err = networkv1alpha1.NewForConfig(&configShallowCopy)
|
cs.networkV1alpha1, err = networkv1alpha1.NewForConfig(&configShallowCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -143,6 +155,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
|
|||||||
var cs Clientset
|
var cs Clientset
|
||||||
cs.devopsV1alpha1 = devopsv1alpha1.NewForConfigOrDie(c)
|
cs.devopsV1alpha1 = devopsv1alpha1.NewForConfigOrDie(c)
|
||||||
cs.devopsV1alpha3 = devopsv1alpha3.NewForConfigOrDie(c)
|
cs.devopsV1alpha3 = devopsv1alpha3.NewForConfigOrDie(c)
|
||||||
|
cs.iamV1alpha2 = iamv1alpha2.NewForConfigOrDie(c)
|
||||||
cs.networkV1alpha1 = networkv1alpha1.NewForConfigOrDie(c)
|
cs.networkV1alpha1 = networkv1alpha1.NewForConfigOrDie(c)
|
||||||
cs.servicemeshV1alpha2 = servicemeshv1alpha2.NewForConfigOrDie(c)
|
cs.servicemeshV1alpha2 = servicemeshv1alpha2.NewForConfigOrDie(c)
|
||||||
cs.tenantV1alpha1 = tenantv1alpha1.NewForConfigOrDie(c)
|
cs.tenantV1alpha1 = tenantv1alpha1.NewForConfigOrDie(c)
|
||||||
@@ -157,6 +170,7 @@ func New(c rest.Interface) *Clientset {
|
|||||||
var cs Clientset
|
var cs Clientset
|
||||||
cs.devopsV1alpha1 = devopsv1alpha1.New(c)
|
cs.devopsV1alpha1 = devopsv1alpha1.New(c)
|
||||||
cs.devopsV1alpha3 = devopsv1alpha3.New(c)
|
cs.devopsV1alpha3 = devopsv1alpha3.New(c)
|
||||||
|
cs.iamV1alpha2 = iamv1alpha2.New(c)
|
||||||
cs.networkV1alpha1 = networkv1alpha1.New(c)
|
cs.networkV1alpha1 = networkv1alpha1.New(c)
|
||||||
cs.servicemeshV1alpha2 = servicemeshv1alpha2.New(c)
|
cs.servicemeshV1alpha2 = servicemeshv1alpha2.New(c)
|
||||||
cs.tenantV1alpha1 = tenantv1alpha1.New(c)
|
cs.tenantV1alpha1 = tenantv1alpha1.New(c)
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import (
|
|||||||
fakedevopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake"
|
fakedevopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake"
|
||||||
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3"
|
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3"
|
||||||
fakedevopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake"
|
fakedevopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2"
|
||||||
|
fakeiamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake"
|
||||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1"
|
networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1"
|
||||||
fakenetworkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1/fake"
|
fakenetworkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1/fake"
|
||||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
|
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
|
||||||
@@ -96,6 +98,11 @@ func (c *Clientset) DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface {
|
|||||||
return &fakedevopsv1alpha3.FakeDevopsV1alpha3{Fake: &c.Fake}
|
return &fakedevopsv1alpha3.FakeDevopsV1alpha3{Fake: &c.Fake}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IamV1alpha2 retrieves the IamV1alpha2Client
|
||||||
|
func (c *Clientset) IamV1alpha2() iamv1alpha2.IamV1alpha2Interface {
|
||||||
|
return &fakeiamv1alpha2.FakeIamV1alpha2{Fake: &c.Fake}
|
||||||
|
}
|
||||||
|
|
||||||
// NetworkV1alpha1 retrieves the NetworkV1alpha1Client
|
// NetworkV1alpha1 retrieves the NetworkV1alpha1Client
|
||||||
func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface {
|
func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface {
|
||||||
return &fakenetworkv1alpha1.FakeNetworkV1alpha1{Fake: &c.Fake}
|
return &fakenetworkv1alpha1.FakeNetworkV1alpha1{Fake: &c.Fake}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||||
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
|
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
@@ -38,6 +39,7 @@ var parameterCodec = runtime.NewParameterCodec(scheme)
|
|||||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||||
devopsv1alpha1.AddToScheme,
|
devopsv1alpha1.AddToScheme,
|
||||||
devopsv1alpha3.AddToScheme,
|
devopsv1alpha3.AddToScheme,
|
||||||
|
iamv1alpha2.AddToScheme,
|
||||||
networkv1alpha1.AddToScheme,
|
networkv1alpha1.AddToScheme,
|
||||||
servicemeshv1alpha2.AddToScheme,
|
servicemeshv1alpha2.AddToScheme,
|
||||||
tenantv1alpha1.AddToScheme,
|
tenantv1alpha1.AddToScheme,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||||
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
|
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
@@ -38,6 +39,7 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
|
|||||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||||
devopsv1alpha1.AddToScheme,
|
devopsv1alpha1.AddToScheme,
|
||||||
devopsv1alpha3.AddToScheme,
|
devopsv1alpha3.AddToScheme,
|
||||||
|
iamv1alpha2.AddToScheme,
|
||||||
networkv1alpha1.AddToScheme,
|
networkv1alpha1.AddToScheme,
|
||||||
servicemeshv1alpha2.AddToScheme,
|
servicemeshv1alpha2.AddToScheme,
|
||||||
tenantv1alpha1.AddToScheme,
|
tenantv1alpha1.AddToScheme,
|
||||||
|
|||||||
20
pkg/client/clientset/versioned/typed/iam/v1alpha2/doc.go
Normal file
20
pkg/client/clientset/versioned/typed/iam/v1alpha2/doc.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
// This package has the automatically generated typed clients.
|
||||||
|
package v1alpha2
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Package fake has the automatically generated clients.
|
||||||
|
package fake
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
testing "k8s.io/client-go/testing"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeIamV1alpha2 struct {
|
||||||
|
*testing.Fake
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeIamV1alpha2) Users() v1alpha2.UserInterface {
|
||||||
|
return &FakeUsers{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTClient returns a RESTClient that is used to communicate
|
||||||
|
// with API server by this client implementation.
|
||||||
|
func (c *FakeIamV1alpha2) RESTClient() rest.Interface {
|
||||||
|
var ret *rest.RESTClient
|
||||||
|
return ret
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
labels "k8s.io/apimachinery/pkg/labels"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
testing "k8s.io/client-go/testing"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeUsers implements UserInterface
|
||||||
|
type FakeUsers struct {
|
||||||
|
Fake *FakeIamV1alpha2
|
||||||
|
}
|
||||||
|
|
||||||
|
var usersResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "users"}
|
||||||
|
|
||||||
|
var usersKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "User"}
|
||||||
|
|
||||||
|
// Get takes name of the user, and returns the corresponding user object, and an error if there is any.
|
||||||
|
func (c *FakeUsers) Get(name string, options v1.GetOptions) (result *v1alpha2.User, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootGetAction(usersResource, name), &v1alpha2.User{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.User), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of Users that match those selectors.
|
||||||
|
func (c *FakeUsers) List(opts v1.ListOptions) (result *v1alpha2.UserList, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootListAction(usersResource, usersKind, opts), &v1alpha2.UserList{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||||
|
if label == nil {
|
||||||
|
label = labels.Everything()
|
||||||
|
}
|
||||||
|
list := &v1alpha2.UserList{ListMeta: obj.(*v1alpha2.UserList).ListMeta}
|
||||||
|
for _, item := range obj.(*v1alpha2.UserList).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 users.
|
||||||
|
func (c *FakeUsers) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||||
|
return c.Fake.
|
||||||
|
InvokesWatch(testing.NewRootWatchAction(usersResource, opts))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a user and creates it. Returns the server's representation of the user, and an error, if there is any.
|
||||||
|
func (c *FakeUsers) Create(user *v1alpha2.User) (result *v1alpha2.User, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootCreateAction(usersResource, user), &v1alpha2.User{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.User), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a user and updates it. Returns the server's representation of the user, and an error, if there is any.
|
||||||
|
func (c *FakeUsers) Update(user *v1alpha2.User) (result *v1alpha2.User, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootUpdateAction(usersResource, user), &v1alpha2.User{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.User), 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 *FakeUsers) UpdateStatus(user *v1alpha2.User) (*v1alpha2.User, error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootUpdateSubresourceAction(usersResource, "status", user), &v1alpha2.User{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.User), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the user and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *FakeUsers) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
_, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootDeleteAction(usersResource, name), &v1alpha2.User{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *FakeUsers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||||
|
action := testing.NewRootDeleteCollectionAction(usersResource, listOptions)
|
||||||
|
|
||||||
|
_, err := c.Fake.Invokes(action, &v1alpha2.UserList{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched user.
|
||||||
|
func (c *FakeUsers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.User, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewRootPatchSubresourceAction(usersResource, name, pt, data, subresources...), &v1alpha2.User{})
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.User), err
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
type UserExpansion interface{}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IamV1alpha2Interface interface {
|
||||||
|
RESTClient() rest.Interface
|
||||||
|
UsersGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
// IamV1alpha2Client is used to interact with features provided by the iam.kubesphere.io group.
|
||||||
|
type IamV1alpha2Client struct {
|
||||||
|
restClient rest.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *IamV1alpha2Client) Users() UserInterface {
|
||||||
|
return newUsers(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForConfig creates a new IamV1alpha2Client for the given config.
|
||||||
|
func NewForConfig(c *rest.Config) (*IamV1alpha2Client, error) {
|
||||||
|
config := *c
|
||||||
|
if err := setConfigDefaults(&config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client, err := rest.RESTClientFor(&config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &IamV1alpha2Client{client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForConfigOrDie creates a new IamV1alpha2Client for the given config and
|
||||||
|
// panics if there is an error in the config.
|
||||||
|
func NewForConfigOrDie(c *rest.Config) *IamV1alpha2Client {
|
||||||
|
client, err := NewForConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new IamV1alpha2Client for the given RESTClient.
|
||||||
|
func New(c rest.Interface) *IamV1alpha2Client {
|
||||||
|
return &IamV1alpha2Client{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setConfigDefaults(config *rest.Config) error {
|
||||||
|
gv := v1alpha2.SchemeGroupVersion
|
||||||
|
config.GroupVersion = &gv
|
||||||
|
config.APIPath = "/apis"
|
||||||
|
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
|
||||||
|
|
||||||
|
if config.UserAgent == "" {
|
||||||
|
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTClient returns a RESTClient that is used to communicate
|
||||||
|
// with API server by this client implementation.
|
||||||
|
func (c *IamV1alpha2Client) RESTClient() rest.Interface {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.restClient
|
||||||
|
}
|
||||||
180
pkg/client/clientset/versioned/typed/iam/v1alpha2/user.go
Normal file
180
pkg/client/clientset/versioned/typed/iam/v1alpha2/user.go
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UsersGetter has a method to return a UserInterface.
|
||||||
|
// A group's client should implement this interface.
|
||||||
|
type UsersGetter interface {
|
||||||
|
Users() UserInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserInterface has methods to work with User resources.
|
||||||
|
type UserInterface interface {
|
||||||
|
Create(*v1alpha2.User) (*v1alpha2.User, error)
|
||||||
|
Update(*v1alpha2.User) (*v1alpha2.User, error)
|
||||||
|
UpdateStatus(*v1alpha2.User) (*v1alpha2.User, error)
|
||||||
|
Delete(name string, options *v1.DeleteOptions) error
|
||||||
|
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
|
||||||
|
Get(name string, options v1.GetOptions) (*v1alpha2.User, error)
|
||||||
|
List(opts v1.ListOptions) (*v1alpha2.UserList, error)
|
||||||
|
Watch(opts v1.ListOptions) (watch.Interface, error)
|
||||||
|
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.User, err error)
|
||||||
|
UserExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// users implements UserInterface
|
||||||
|
type users struct {
|
||||||
|
client rest.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUsers returns a Users
|
||||||
|
func newUsers(c *IamV1alpha2Client) *users {
|
||||||
|
return &users{
|
||||||
|
client: c.RESTClient(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get takes name of the user, and returns the corresponding user object, and an error if there is any.
|
||||||
|
func (c *users) Get(name string, options v1.GetOptions) (result *v1alpha2.User, err error) {
|
||||||
|
result = &v1alpha2.User{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Resource("users").
|
||||||
|
Name(name).
|
||||||
|
VersionedParams(&options, scheme.ParameterCodec).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of Users that match those selectors.
|
||||||
|
func (c *users) List(opts v1.ListOptions) (result *v1alpha2.UserList, err error) {
|
||||||
|
var timeout time.Duration
|
||||||
|
if opts.TimeoutSeconds != nil {
|
||||||
|
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||||
|
}
|
||||||
|
result = &v1alpha2.UserList{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Resource("users").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Timeout(timeout).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns a watch.Interface that watches the requested users.
|
||||||
|
func (c *users) 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("users").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Timeout(timeout).
|
||||||
|
Watch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a user and creates it. Returns the server's representation of the user, and an error, if there is any.
|
||||||
|
func (c *users) Create(user *v1alpha2.User) (result *v1alpha2.User, err error) {
|
||||||
|
result = &v1alpha2.User{}
|
||||||
|
err = c.client.Post().
|
||||||
|
Resource("users").
|
||||||
|
Body(user).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a user and updates it. Returns the server's representation of the user, and an error, if there is any.
|
||||||
|
func (c *users) Update(user *v1alpha2.User) (result *v1alpha2.User, err error) {
|
||||||
|
result = &v1alpha2.User{}
|
||||||
|
err = c.client.Put().
|
||||||
|
Resource("users").
|
||||||
|
Name(user.Name).
|
||||||
|
Body(user).
|
||||||
|
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 *users) UpdateStatus(user *v1alpha2.User) (result *v1alpha2.User, err error) {
|
||||||
|
result = &v1alpha2.User{}
|
||||||
|
err = c.client.Put().
|
||||||
|
Resource("users").
|
||||||
|
Name(user.Name).
|
||||||
|
SubResource("status").
|
||||||
|
Body(user).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the user and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *users) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
return c.client.Delete().
|
||||||
|
Resource("users").
|
||||||
|
Name(name).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *users) 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("users").
|
||||||
|
VersionedParams(&listOptions, scheme.ParameterCodec).
|
||||||
|
Timeout(timeout).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched user.
|
||||||
|
func (c *users) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.User, err error) {
|
||||||
|
result = &v1alpha2.User{}
|
||||||
|
err = c.client.Patch(pt).
|
||||||
|
Resource("users").
|
||||||
|
SubResource(subresources...).
|
||||||
|
Name(name).
|
||||||
|
Body(data).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
cache "k8s.io/client-go/tools/cache"
|
cache "k8s.io/client-go/tools/cache"
|
||||||
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
devops "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops"
|
devops "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops"
|
||||||
|
iam "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam"
|
||||||
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
|
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
|
||||||
network "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network"
|
network "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network"
|
||||||
servicemesh "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh"
|
servicemesh "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh"
|
||||||
@@ -177,6 +178,7 @@ type SharedInformerFactory interface {
|
|||||||
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
|
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
|
||||||
|
|
||||||
Devops() devops.Interface
|
Devops() devops.Interface
|
||||||
|
Iam() iam.Interface
|
||||||
Network() network.Interface
|
Network() network.Interface
|
||||||
Servicemesh() servicemesh.Interface
|
Servicemesh() servicemesh.Interface
|
||||||
Tenant() tenant.Interface
|
Tenant() tenant.Interface
|
||||||
@@ -187,6 +189,10 @@ func (f *sharedInformerFactory) Devops() devops.Interface {
|
|||||||
return devops.New(f, f.namespace, f.tweakListOptions)
|
return devops.New(f, f.namespace, f.tweakListOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *sharedInformerFactory) Iam() iam.Interface {
|
||||||
|
return iam.New(f, f.namespace, f.tweakListOptions)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *sharedInformerFactory) Network() network.Interface {
|
func (f *sharedInformerFactory) Network() network.Interface {
|
||||||
return network.New(f, f.namespace, f.tweakListOptions)
|
return network.New(f, f.namespace, f.tweakListOptions)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ import (
|
|||||||
cache "k8s.io/client-go/tools/cache"
|
cache "k8s.io/client-go/tools/cache"
|
||||||
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||||
v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
|
v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||||
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
towerv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tower/v1alpha1"
|
towerv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tower/v1alpha1"
|
||||||
)
|
)
|
||||||
@@ -73,6 +74,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
|||||||
case v1alpha3.SchemeGroupVersion.WithResource("pipelines"):
|
case v1alpha3.SchemeGroupVersion.WithResource("pipelines"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha3().Pipelines().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha3().Pipelines().Informer()}, nil
|
||||||
|
|
||||||
|
// Group=iam.kubesphere.io, Version=v1alpha2
|
||||||
|
case v1alpha2.SchemeGroupVersion.WithResource("users"):
|
||||||
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Users().Informer()}, nil
|
||||||
|
|
||||||
// Group=network.kubesphere.io, Version=v1alpha1
|
// Group=network.kubesphere.io, Version=v1alpha1
|
||||||
case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"):
|
case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().NamespaceNetworkPolicies().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().NamespaceNetworkPolicies().Informer()}, nil
|
||||||
@@ -80,9 +85,9 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
|||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().WorkspaceNetworkPolicies().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().WorkspaceNetworkPolicies().Informer()}, nil
|
||||||
|
|
||||||
// Group=servicemesh.kubesphere.io, Version=v1alpha2
|
// Group=servicemesh.kubesphere.io, Version=v1alpha2
|
||||||
case v1alpha2.SchemeGroupVersion.WithResource("servicepolicies"):
|
case servicemeshv1alpha2.SchemeGroupVersion.WithResource("servicepolicies"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().ServicePolicies().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().ServicePolicies().Informer()}, nil
|
||||||
case v1alpha2.SchemeGroupVersion.WithResource("strategies"):
|
case servicemeshv1alpha2.SchemeGroupVersion.WithResource("strategies"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().Strategies().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().Strategies().Informer()}, nil
|
||||||
|
|
||||||
// Group=tenant.kubesphere.io, Version=v1alpha1
|
// Group=tenant.kubesphere.io, Version=v1alpha1
|
||||||
|
|||||||
46
pkg/client/informers/externalversions/iam/interface.go
Normal file
46
pkg/client/informers/externalversions/iam/interface.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package iam
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
|
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface provides access to each of this group's versions.
|
||||||
|
type Interface interface {
|
||||||
|
// V1alpha2 provides access to shared informers for resources in V1alpha2.
|
||||||
|
V1alpha2() v1alpha2.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
type group struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
namespace string
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Interface.
|
||||||
|
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
|
||||||
|
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
|
||||||
|
}
|
||||||
|
|
||||||
|
// V1alpha2 returns a new v1alpha2.Interface.
|
||||||
|
func (g *group) V1alpha2() v1alpha2.Interface {
|
||||||
|
return v1alpha2.New(g.factory, g.namespace, g.tweakListOptions)
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface provides access to all the informers in this group version.
|
||||||
|
type Interface interface {
|
||||||
|
// Users returns a UserInformer.
|
||||||
|
Users() UserInformer
|
||||||
|
}
|
||||||
|
|
||||||
|
type version struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
namespace string
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Interface.
|
||||||
|
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
|
||||||
|
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Users returns a UserInformer.
|
||||||
|
func (v *version) Users() UserInformer {
|
||||||
|
return &userInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
||||||
|
}
|
||||||
88
pkg/client/informers/externalversions/iam/v1alpha2/user.go
Normal file
88
pkg/client/informers/externalversions/iam/v1alpha2/user.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
cache "k8s.io/client-go/tools/cache"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
|
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInformer provides access to a shared informer and lister for
|
||||||
|
// Users.
|
||||||
|
type UserInformer interface {
|
||||||
|
Informer() cache.SharedIndexInformer
|
||||||
|
Lister() v1alpha2.UserLister
|
||||||
|
}
|
||||||
|
|
||||||
|
type userInformer struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserInformer constructs a new informer for User 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 NewUserInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredUserInformer(client, resyncPeriod, indexers, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilteredUserInformer constructs a new informer for User 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 NewFilteredUserInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
|
||||||
|
return cache.NewSharedIndexInformer(
|
||||||
|
&cache.ListWatch{
|
||||||
|
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.IamV1alpha2().Users().List(options)
|
||||||
|
},
|
||||||
|
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.IamV1alpha2().Users().Watch(options)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&iamv1alpha2.User{},
|
||||||
|
resyncPeriod,
|
||||||
|
indexers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *userInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredUserInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *userInformer) Informer() cache.SharedIndexInformer {
|
||||||
|
return f.factory.InformerFor(&iamv1alpha2.User{}, f.defaultInformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *userInformer) Lister() v1alpha2.UserLister {
|
||||||
|
return v1alpha2.NewUserLister(f.Informer().GetIndexer())
|
||||||
|
}
|
||||||
23
pkg/client/listers/iam/v1alpha2/expansion_generated.go
Normal file
23
pkg/client/listers/iam/v1alpha2/expansion_generated.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by lister-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
// UserListerExpansion allows custom methods to be added to
|
||||||
|
// UserLister.
|
||||||
|
type UserListerExpansion interface{}
|
||||||
65
pkg/client/listers/iam/v1alpha2/user.go
Normal file
65
pkg/client/listers/iam/v1alpha2/user.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by lister-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserLister helps list Users.
|
||||||
|
type UserLister interface {
|
||||||
|
// List lists all Users in the indexer.
|
||||||
|
List(selector labels.Selector) (ret []*v1alpha2.User, err error)
|
||||||
|
// Get retrieves the User from the index for a given name.
|
||||||
|
Get(name string) (*v1alpha2.User, error)
|
||||||
|
UserListerExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// userLister implements the UserLister interface.
|
||||||
|
type userLister struct {
|
||||||
|
indexer cache.Indexer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUserLister returns a new UserLister.
|
||||||
|
func NewUserLister(indexer cache.Indexer) UserLister {
|
||||||
|
return &userLister{indexer: indexer}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all Users in the indexer.
|
||||||
|
func (s *userLister) List(selector labels.Selector) (ret []*v1alpha2.User, err error) {
|
||||||
|
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
|
||||||
|
ret = append(ret, m.(*v1alpha2.User))
|
||||||
|
})
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the User from the index for a given name.
|
||||||
|
func (s *userLister) Get(name string) (*v1alpha2.User, error) {
|
||||||
|
obj, exists, err := s.indexer.GetByKey(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.NewNotFound(v1alpha2.Resource("user"), name)
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha2.User), nil
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ import (
|
|||||||
"github.com/golang/protobuf/ptypes/wrappers"
|
"github.com/golang/protobuf/ptypes/wrappers"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbac "k8s.io/api/rbac/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@@ -44,22 +43,6 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
adminDescription = "Allows admin access to perform any action on any resource, it gives full control over every resource in the namespace."
|
|
||||||
operatorDescription = "The maintainer of the namespace who can manage resources other than users and roles in the namespace."
|
|
||||||
viewerDescription = "Allows viewer access to view all resources in the namespace."
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
admin = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "admin", Annotations: map[string]string{constants.DescriptionAnnotationKey: adminDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}}
|
|
||||||
operator = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "operator", Annotations: map[string]string{constants.DescriptionAnnotationKey: operatorDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}},
|
|
||||||
{Verbs: []string{"*"}, APIGroups: []string{"apps", "extensions", "batch", "logging.kubesphere.io", "monitoring.kubesphere.io", "iam.kubesphere.io", "autoscaling", "alerting.kubesphere.io", "openpitrix.io", "app.k8s.io", "servicemesh.kubesphere.io", "operations.kubesphere.io", "devops.kubesphere.io"}, Resources: []string{"*"}},
|
|
||||||
{Verbs: []string{"*"}, APIGroups: []string{"", "resources.kubesphere.io"}, Resources: []string{"jobs", "cronjobs", "daemonsets", "deployments", "horizontalpodautoscalers", "ingresses", "endpoints", "configmaps", "events", "persistentvolumeclaims", "pods", "podtemplates", "pods", "secrets", "services"}},
|
|
||||||
}}
|
|
||||||
viewer = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "viewer", Annotations: map[string]string{constants.DescriptionAnnotationKey: viewerDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}}
|
|
||||||
defaultRoles = []rbac.Role{admin, operator, viewer}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
|
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
|
||||||
* business logic. Delete these comments after modifying this file.*
|
* business logic. Delete these comments after modifying this file.*
|
||||||
@@ -164,19 +147,6 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res
|
|||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
controlledByWorkspace, err := r.isControlledByWorkspace(instance)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !controlledByWorkspace {
|
|
||||||
|
|
||||||
err = r.deleteRoleBindings(instance)
|
|
||||||
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = r.checkAndBindWorkspace(instance); err != nil {
|
if err = r.checkAndBindWorkspace(instance); err != nil {
|
||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
@@ -357,24 +327,3 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error {
|
|||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileNamespace) deleteRoleBindings(namespace *corev1.Namespace) error {
|
|
||||||
klog.V(4).Info("deleting role bindings namespace: ", namespace.Name)
|
|
||||||
adminBinding := &rbac.RoleBinding{}
|
|
||||||
adminBinding.Name = admin.Name
|
|
||||||
adminBinding.Namespace = namespace.Name
|
|
||||||
err := r.Delete(context.TODO(), adminBinding)
|
|
||||||
if err != nil && !errors.IsNotFound(err) {
|
|
||||||
klog.Errorf("deleting role binding namespace: %s, role binding: %s,error: %s", namespace.Name, adminBinding.Name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
viewerBinding := &rbac.RoleBinding{}
|
|
||||||
viewerBinding.Name = viewer.Name
|
|
||||||
viewerBinding.Namespace = namespace.Name
|
|
||||||
err = r.Delete(context.TODO(), viewerBinding)
|
|
||||||
if err != nil && !errors.IsNotFound(err) {
|
|
||||||
klog.Errorf("deleting role binding namespace: %s,role binding: %s,error: %s", namespace.Name, viewerBinding.Name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
230
pkg/controller/user/user_controller.go
Normal file
230
pkg/controller/user/user_controller.go
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
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"
|
||||||
|
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||||
|
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
|
||||||
|
userinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
|
||||||
|
userlister "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SuccessSynced is used as part of the Event 'reason' when a Foo is synced
|
||||||
|
successSynced = "Synced"
|
||||||
|
// is synced successfully
|
||||||
|
messageResourceSynced = "User synced successfully"
|
||||||
|
controllerName = "user-controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
kubeClientset kubernetes.Interface
|
||||||
|
kubesphereClientset kubesphereclient.Interface
|
||||||
|
|
||||||
|
userInformer userinformer.UserInformer
|
||||||
|
userLister userlister.UserLister
|
||||||
|
userSynced cache.InformerSynced
|
||||||
|
// 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(kubeclientset kubernetes.Interface,
|
||||||
|
kubesphereklientset kubesphereclient.Interface,
|
||||||
|
userInformer userinformer.UserInformer) *Controller {
|
||||||
|
// Create event broadcaster
|
||||||
|
// Add sample-controller types to the default Kubernetes Scheme so Events can be
|
||||||
|
// logged for sample-controller types.
|
||||||
|
|
||||||
|
utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme))
|
||||||
|
klog.V(4).Info("Creating event broadcaster")
|
||||||
|
eventBroadcaster := record.NewBroadcaster()
|
||||||
|
eventBroadcaster.StartLogging(klog.Infof)
|
||||||
|
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")})
|
||||||
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName})
|
||||||
|
ctl := &Controller{
|
||||||
|
kubeClientset: kubeclientset,
|
||||||
|
kubesphereClientset: kubesphereklientset,
|
||||||
|
userInformer: userInformer,
|
||||||
|
userLister: userInformer.Lister(),
|
||||||
|
userSynced: userInformer.Informer().HasSynced,
|
||||||
|
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Users"),
|
||||||
|
recorder: recorder,
|
||||||
|
}
|
||||||
|
klog.Info("Setting up event handlers")
|
||||||
|
userInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: ctl.enqueueUser,
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
ctl.enqueueUser(new)
|
||||||
|
},
|
||||||
|
DeleteFunc: ctl.enqueueUser,
|
||||||
|
})
|
||||||
|
return ctl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
||||||
|
defer utilruntime.HandleCrash()
|
||||||
|
defer c.workqueue.ShutDown()
|
||||||
|
|
||||||
|
//init client
|
||||||
|
|
||||||
|
// Start the informer factories to begin populating the informer caches
|
||||||
|
klog.Info("Starting User controller")
|
||||||
|
|
||||||
|
// Wait for the caches to be synced before starting workers
|
||||||
|
klog.Info("Waiting for informer caches to sync")
|
||||||
|
if ok := cache.WaitForCacheSync(stopCh, c.userSynced); !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) enqueueUser(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 {
|
||||||
|
|
||||||
|
// Get the user with this name
|
||||||
|
user, err := c.userLister.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("user '%s' in work queue no longer exists", key))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.updateUserStatus(user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.recorder.Event(user, corev1.EventTypeNormal, successSynced, messageResourceSynced)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateUserStatus(user *iamv1alpha2.User) error {
|
||||||
|
userCopy := user.DeepCopy()
|
||||||
|
userCopy.Status.State = iamv1alpha2.UserActive
|
||||||
|
_, err := c.kubesphereClientset.IamV1alpha2().Users().Update(userCopy)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Start(stopCh <-chan struct{}) error {
|
||||||
|
return c.Run(4, stopCh)
|
||||||
|
}
|
||||||
245
pkg/controller/user/user_controller_test.go
Normal file
245
pkg/controller/user/user_controller_test.go
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
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 user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
|
k8sfake "k8s.io/client-go/kubernetes/fake"
|
||||||
|
core "k8s.io/client-go/testing"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||||
|
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
|
||||||
|
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
alwaysReady = func() bool { return true }
|
||||||
|
noResyncPeriodFunc = func() time.Duration { return 0 }
|
||||||
|
)
|
||||||
|
|
||||||
|
type fixture struct {
|
||||||
|
t *testing.T
|
||||||
|
|
||||||
|
client *fake.Clientset
|
||||||
|
kubeclient *k8sfake.Clientset
|
||||||
|
// Objects to put in the store.
|
||||||
|
userLister []*iamv1alpha2.User
|
||||||
|
// Actions expected to happen on the client.
|
||||||
|
kubeactions []core.Action
|
||||||
|
actions []core.Action
|
||||||
|
// Objects from here preloaded into NewSimpleFake.
|
||||||
|
kubeobjects []runtime.Object
|
||||||
|
objects []runtime.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFixture(t *testing.T) *fixture {
|
||||||
|
f := &fixture{}
|
||||||
|
f.t = t
|
||||||
|
f.objects = []runtime.Object{}
|
||||||
|
f.kubeobjects = []runtime.Object{}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUser(name string) *iamv1alpha2.User {
|
||||||
|
return &iamv1alpha2.User{
|
||||||
|
TypeMeta: metav1.TypeMeta{APIVersion: iamv1alpha2.SchemeGroupVersion.String()},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: iamv1alpha2.UserSpec{
|
||||||
|
Email: fmt.Sprintf("%s@kubesphere.io", name),
|
||||||
|
Lang: "zh-CN",
|
||||||
|
Description: "fake user",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fixture) newController() (*Controller, informers.SharedInformerFactory, kubeinformers.SharedInformerFactory) {
|
||||||
|
f.client = fake.NewSimpleClientset(f.objects...)
|
||||||
|
f.kubeclient = k8sfake.NewSimpleClientset(f.kubeobjects...)
|
||||||
|
|
||||||
|
i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc())
|
||||||
|
k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc())
|
||||||
|
|
||||||
|
for _, user := range f.userLister {
|
||||||
|
err := i.Iam().V1alpha2().Users().Informer().GetIndexer().Add(user)
|
||||||
|
if err != nil {
|
||||||
|
f.t.Errorf("add user:%s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c := NewController(f.kubeclient, f.client, i.Iam().V1alpha2().Users())
|
||||||
|
c.userSynced = alwaysReady
|
||||||
|
c.recorder = &record.FakeRecorder{}
|
||||||
|
|
||||||
|
return c, i, k8sI
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fixture) run(userName string) {
|
||||||
|
f.runController(userName, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fixture) runExpectError(userName string) {
|
||||||
|
f.runController(userName, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fixture) runController(user string, startInformers bool, expectError bool) {
|
||||||
|
c, i, k8sI := f.newController()
|
||||||
|
if startInformers {
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
i.Start(stopCh)
|
||||||
|
k8sI.Start(stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.reconcile(user)
|
||||||
|
if !expectError && err != nil {
|
||||||
|
f.t.Errorf("error syncing user: %v", err)
|
||||||
|
} else if expectError && err == nil {
|
||||||
|
f.t.Error("expected error syncing user, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
actions := filterInformerActions(f.client.Actions())
|
||||||
|
for i, action := range actions {
|
||||||
|
if len(f.actions) < i+1 {
|
||||||
|
f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedAction := f.actions[i]
|
||||||
|
checkAction(expectedAction, action, f.t)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.actions) > len(actions) {
|
||||||
|
f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):])
|
||||||
|
}
|
||||||
|
|
||||||
|
k8sActions := filterInformerActions(f.kubeclient.Actions())
|
||||||
|
for i, action := range k8sActions {
|
||||||
|
if len(f.kubeactions) < i+1 {
|
||||||
|
f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedAction := f.kubeactions[i]
|
||||||
|
checkAction(expectedAction, action, f.t)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.kubeactions) > len(k8sActions) {
|
||||||
|
f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkAction verifies that expected and actual actions are equal and both have
|
||||||
|
// same attached resources
|
||||||
|
func checkAction(expected, actual core.Action, t *testing.T) {
|
||||||
|
if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) {
|
||||||
|
t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.TypeOf(actual) != reflect.TypeOf(expected) {
|
||||||
|
t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a := actual.(type) {
|
||||||
|
case core.CreateActionImpl:
|
||||||
|
e, _ := expected.(core.CreateActionImpl)
|
||||||
|
expObject := e.GetObject()
|
||||||
|
object := a.GetObject()
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expObject, object) {
|
||||||
|
t.Errorf("Action %s %s has wrong object\nDiff:\n %s",
|
||||||
|
a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object))
|
||||||
|
}
|
||||||
|
case core.UpdateActionImpl:
|
||||||
|
e, _ := expected.(core.UpdateActionImpl)
|
||||||
|
expObject := e.GetObject()
|
||||||
|
object := a.GetObject()
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expObject, object) {
|
||||||
|
t.Errorf("Action %s %s has wrong object\nDiff:\n %s",
|
||||||
|
a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object))
|
||||||
|
}
|
||||||
|
case core.PatchActionImpl:
|
||||||
|
e, _ := expected.(core.PatchActionImpl)
|
||||||
|
expPatch := e.GetPatch()
|
||||||
|
patch := a.GetPatch()
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expPatch, patch) {
|
||||||
|
t.Errorf("Action %s %s has wrong patch\nDiff:\n %s",
|
||||||
|
a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it",
|
||||||
|
actual.GetVerb(), actual.GetResource().Resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterInformerActions filters list and watch actions for testing resources.
|
||||||
|
// Since list and watch don't change resource state we can filter it to lower
|
||||||
|
// nose level in our tests.
|
||||||
|
func filterInformerActions(actions []core.Action) []core.Action {
|
||||||
|
var ret []core.Action
|
||||||
|
for _, action := range actions {
|
||||||
|
if action.Matches("list", "users") ||
|
||||||
|
action.Matches("watch", "users") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ret = append(ret, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fixture) expectUpdateUserStatusAction(user *iamv1alpha2.User) {
|
||||||
|
expect := user.DeepCopy()
|
||||||
|
expect.Status.State = iamv1alpha2.UserActive
|
||||||
|
action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "users"}, "", expect)
|
||||||
|
f.actions = append(f.actions, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKey(user *iamv1alpha2.User, t *testing.T) string {
|
||||||
|
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(user)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error getting key for user %v: %v", user.Name, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoNothing(t *testing.T) {
|
||||||
|
f := newFixture(t)
|
||||||
|
user := newUser("test")
|
||||||
|
|
||||||
|
f.userLister = append(f.userLister, user)
|
||||||
|
f.objects = append(f.objects, user)
|
||||||
|
|
||||||
|
f.expectUpdateUserStatusAction(user)
|
||||||
|
f.run(getKey(user, t))
|
||||||
|
}
|
||||||
@@ -20,30 +20,17 @@ package workspace
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
rbac "k8s.io/api/rbac/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
log "k8s.io/klog"
|
|
||||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||||
"kubesphere.io/kubesphere/pkg/constants"
|
|
||||||
"kubesphere.io/kubesphere/pkg/models"
|
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
|
|
||||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||||
"reflect"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -54,14 +41,14 @@ const (
|
|||||||
|
|
||||||
// Add creates a new Workspace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
// 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.
|
// and Start it when the Manager is Started.
|
||||||
func Add(mgr manager.Manager, kubesphereClient kubesphere.Interface) error {
|
func Add(mgr manager.Manager) error {
|
||||||
return add(mgr, newReconciler(mgr, kubesphereClient))
|
return add(mgr, newReconciler(mgr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// newReconciler returns a new reconcile.Reconciler
|
// newReconciler returns a new reconcile.Reconciler
|
||||||
func newReconciler(mgr manager.Manager, kubesphereClient kubesphere.Interface) reconcile.Reconciler {
|
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
|
||||||
return &ReconcileWorkspace{Client: mgr.GetClient(), scheme: mgr.GetScheme(),
|
return &ReconcileWorkspace{Client: mgr.GetClient(), scheme: mgr.GetScheme(),
|
||||||
recorder: mgr.GetEventRecorderFor("workspace-controller"), ksclient: kubesphereClient}
|
recorder: mgr.GetEventRecorderFor("workspace-controller")}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add adds a new Controller to mgr with r as the reconcile.Reconciler
|
// add adds a new Controller to mgr with r as the reconcile.Reconciler
|
||||||
@@ -88,7 +75,6 @@ type ReconcileWorkspace struct {
|
|||||||
client.Client
|
client.Client
|
||||||
scheme *runtime.Scheme
|
scheme *runtime.Scheme
|
||||||
recorder record.EventRecorder
|
recorder record.EventRecorder
|
||||||
ksclient kubesphere.Interface
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconcile reads that state of the cluster for a Workspace object and makes changes based on the state read
|
// Reconcile reads that state of the cluster for a Workspace object and makes changes based on the state read
|
||||||
@@ -125,9 +111,6 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res
|
|||||||
// The object is being deleted
|
// The object is being deleted
|
||||||
if sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
|
if sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
|
||||||
// our finalizer is present, so lets handle our external dependency
|
// our finalizer is present, so lets handle our external dependency
|
||||||
if err := r.deleteDevOpsProjects(instance); err != nil {
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove our finalizer from the list and update it.
|
// remove our finalizer from the list and update it.
|
||||||
instance.ObjectMeta.Finalizers = sliceutil.RemoveString(instance.ObjectMeta.Finalizers, func(item string) bool {
|
instance.ObjectMeta.Finalizers = sliceutil.RemoveString(instance.ObjectMeta.Finalizers, func(item string) bool {
|
||||||
@@ -142,497 +125,5 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res
|
|||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = r.createWorkspaceAdmin(instance); err != nil {
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = r.createWorkspaceRegular(instance); err != nil {
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = r.createWorkspaceViewer(instance); err != nil {
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = r.createWorkspaceRoleBindings(instance); err != nil {
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = r.bindNamespaces(instance); err != nil {
|
|
||||||
return reconcile.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return reconcile.Result{}, nil
|
return reconcile.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) createWorkspaceAdmin(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
found := &rbac.ClusterRole{}
|
|
||||||
|
|
||||||
admin := getWorkspaceAdmin(instance.Name)
|
|
||||||
|
|
||||||
if err := controllerutil.SetControllerReference(instance, admin, r.scheme); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.Get(context.TODO(), types.NamespacedName{Name: admin.Name}, found)
|
|
||||||
|
|
||||||
if err != nil && errors.IsNotFound(err) {
|
|
||||||
log.Info("Creating workspace role", "workspace", instance.Name, "name", admin.Name)
|
|
||||||
err = r.Create(context.TODO(), admin)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
found = admin
|
|
||||||
} else if err != nil {
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the found object and write the result back if there are any changes
|
|
||||||
if !reflect.DeepEqual(admin.Rules, found.Rules) || !reflect.DeepEqual(admin.Labels, found.Labels) || !reflect.DeepEqual(admin.Annotations, found.Annotations) {
|
|
||||||
found.Rules = admin.Rules
|
|
||||||
found.Labels = admin.Labels
|
|
||||||
found.Annotations = admin.Annotations
|
|
||||||
log.Info("Updating workspace role", "workspace", instance.Name, "name", admin.Name)
|
|
||||||
err = r.Update(context.TODO(), found)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) createWorkspaceRegular(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
found := &rbac.ClusterRole{}
|
|
||||||
|
|
||||||
regular := getWorkspaceRegular(instance.Name)
|
|
||||||
|
|
||||||
if err := controllerutil.SetControllerReference(instance, regular, r.scheme); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.Get(context.TODO(), types.NamespacedName{Name: regular.Name}, found)
|
|
||||||
|
|
||||||
if err != nil && errors.IsNotFound(err) {
|
|
||||||
|
|
||||||
log.Info("Creating workspace role", "workspace", instance.Name, "name", regular.Name)
|
|
||||||
err = r.Create(context.TODO(), regular)
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
found = regular
|
|
||||||
} else if err != nil {
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the found object and write the result back if there are any changes
|
|
||||||
if !reflect.DeepEqual(regular.Rules, found.Rules) || !reflect.DeepEqual(regular.Labels, found.Labels) || !reflect.DeepEqual(regular.Annotations, found.Annotations) {
|
|
||||||
found.Rules = regular.Rules
|
|
||||||
found.Labels = regular.Labels
|
|
||||||
found.Annotations = regular.Annotations
|
|
||||||
log.Info("Updating workspace role", "workspace", instance.Name, "name", regular.Name)
|
|
||||||
err = r.Update(context.TODO(), found)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) createWorkspaceViewer(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
found := &rbac.ClusterRole{}
|
|
||||||
|
|
||||||
viewer := getWorkspaceViewer(instance.Name)
|
|
||||||
|
|
||||||
if err := controllerutil.SetControllerReference(instance, viewer, r.scheme); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.Get(context.TODO(), types.NamespacedName{Name: viewer.Name}, found)
|
|
||||||
|
|
||||||
if err != nil && errors.IsNotFound(err) {
|
|
||||||
log.Info("Creating workspace role", "workspace", instance.Name, "name", viewer.Name)
|
|
||||||
err = r.Create(context.TODO(), viewer)
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
found = viewer
|
|
||||||
} else if err != nil {
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the found object and write the result back if there are any changes
|
|
||||||
if !reflect.DeepEqual(viewer.Rules, found.Rules) || !reflect.DeepEqual(viewer.Labels, found.Labels) || !reflect.DeepEqual(viewer.Annotations, found.Annotations) {
|
|
||||||
found.Rules = viewer.Rules
|
|
||||||
found.Labels = viewer.Labels
|
|
||||||
found.Annotations = viewer.Annotations
|
|
||||||
log.Info("Updating workspace role", "workspace", instance.Name, "name", viewer.Name)
|
|
||||||
err = r.Update(context.TODO(), found)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) createGroup(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
_, err := r.ksclient.DescribeGroup(instance.Name)
|
|
||||||
|
|
||||||
group := &models.Group{
|
|
||||||
Name: instance.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil && kubesphere.IsNotFound(err) {
|
|
||||||
log.Info("Creating group", "group name", instance.Name)
|
|
||||||
_, err = r.ksclient.CreateGroup(group)
|
|
||||||
if err != nil {
|
|
||||||
if kubesphere.IsExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) deleteGroup(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
log.Info("Creating group", "group name", instance.Name)
|
|
||||||
if err := r.ksclient.DeleteGroup(instance.Name); err != nil {
|
|
||||||
if kubesphere.IsNotFound(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) deleteDevOpsProjects(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
log.Info("Delete DevOps Projects")
|
|
||||||
for {
|
|
||||||
projects, err := r.ksclient.ListWorkspaceDevOpsProjects(instance.Name)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err, "Failed to Get Workspace's DevOps Projects", "ws", instance.Name)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if projects.TotalCount == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
errChan := make(chan error, len(projects.Items))
|
|
||||||
for _, project := range projects.Items {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(workspace, devops string) {
|
|
||||||
err = r.ksclient.DeleteWorkspaceDevOpsProjects(workspace, devops)
|
|
||||||
errChan <- err
|
|
||||||
wg.Done()
|
|
||||||
}(instance.Name, project.ProjectId)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(errChan)
|
|
||||||
for err := range errChan {
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err, "delete devops project error")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) createWorkspaceRoleBindings(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
|
|
||||||
adminRoleBinding := &rbac.ClusterRoleBinding{}
|
|
||||||
adminRoleBinding.Name = getWorkspaceAdminRoleBindingName(instance.Name)
|
|
||||||
adminRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name}
|
|
||||||
adminRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceAdminRoleName(instance.Name)}
|
|
||||||
|
|
||||||
workspaceManager := rbac.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: instance.Spec.Manager}
|
|
||||||
|
|
||||||
if workspaceManager.Name != "" {
|
|
||||||
adminRoleBinding.Subjects = []rbac.Subject{workspaceManager}
|
|
||||||
} else {
|
|
||||||
adminRoleBinding.Subjects = []rbac.Subject{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := controllerutil.SetControllerReference(instance, adminRoleBinding, r.scheme); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
foundRoleBinding := &rbac.ClusterRoleBinding{}
|
|
||||||
|
|
||||||
err := r.Get(context.TODO(), types.NamespacedName{Name: adminRoleBinding.Name}, foundRoleBinding)
|
|
||||||
|
|
||||||
if err != nil && errors.IsNotFound(err) {
|
|
||||||
log.Info("Creating workspace role binding", "workspace", instance.Name, "name", adminRoleBinding.Name)
|
|
||||||
err = r.Create(context.TODO(), adminRoleBinding)
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
foundRoleBinding = adminRoleBinding
|
|
||||||
} else if err != nil {
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the found object and write the result back if there are any changes
|
|
||||||
if !reflect.DeepEqual(adminRoleBinding.RoleRef, foundRoleBinding.RoleRef) {
|
|
||||||
log.Info("Deleting conflict workspace role binding", "workspace", instance.Name, "name", adminRoleBinding.Name)
|
|
||||||
err = r.Delete(context.TODO(), foundRoleBinding)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return fmt.Errorf("conflict workspace role binding %s, waiting for recreate", foundRoleBinding.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if workspaceManager.Name != "" && !hasSubject(foundRoleBinding.Subjects, workspaceManager) {
|
|
||||||
foundRoleBinding.Subjects = append(foundRoleBinding.Subjects, workspaceManager)
|
|
||||||
log.Info("Updating workspace role binding", "workspace", instance.Name, "name", adminRoleBinding.Name)
|
|
||||||
err = r.Update(context.TODO(), foundRoleBinding)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
regularRoleBinding := &rbac.ClusterRoleBinding{}
|
|
||||||
regularRoleBinding.Name = getWorkspaceRegularRoleBindingName(instance.Name)
|
|
||||||
regularRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name}
|
|
||||||
regularRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceRegularRoleName(instance.Name)}
|
|
||||||
regularRoleBinding.Subjects = []rbac.Subject{}
|
|
||||||
|
|
||||||
if err = controllerutil.SetControllerReference(instance, regularRoleBinding, r.scheme); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.Get(context.TODO(), types.NamespacedName{Name: regularRoleBinding.Name}, foundRoleBinding)
|
|
||||||
|
|
||||||
if err != nil && errors.IsNotFound(err) {
|
|
||||||
log.Info("Creating workspace role binding", "workspace", instance.Name, "name", regularRoleBinding.Name)
|
|
||||||
err = r.Create(context.TODO(), regularRoleBinding)
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
foundRoleBinding = regularRoleBinding
|
|
||||||
} else if err != nil {
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the found object and write the result back if there are any changes
|
|
||||||
if !reflect.DeepEqual(regularRoleBinding.RoleRef, foundRoleBinding.RoleRef) {
|
|
||||||
log.Info("Deleting conflict workspace role binding", "workspace", instance.Name, "name", regularRoleBinding.Name)
|
|
||||||
err = r.Delete(context.TODO(), foundRoleBinding)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return fmt.Errorf("conflict workspace role binding %s, waiting for recreate", foundRoleBinding.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
viewerRoleBinding := &rbac.ClusterRoleBinding{}
|
|
||||||
viewerRoleBinding.Name = getWorkspaceViewerRoleBindingName(instance.Name)
|
|
||||||
viewerRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name}
|
|
||||||
viewerRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceViewerRoleName(instance.Name)}
|
|
||||||
viewerRoleBinding.Subjects = []rbac.Subject{}
|
|
||||||
|
|
||||||
if err = controllerutil.SetControllerReference(instance, viewerRoleBinding, r.scheme); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.Get(context.TODO(), types.NamespacedName{Name: viewerRoleBinding.Name}, foundRoleBinding)
|
|
||||||
|
|
||||||
if err != nil && errors.IsNotFound(err) {
|
|
||||||
log.Info("Creating workspace role binding", "workspace", instance.Name, "name", viewerRoleBinding.Name)
|
|
||||||
err = r.Create(context.TODO(), viewerRoleBinding)
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
foundRoleBinding = viewerRoleBinding
|
|
||||||
} else if err != nil {
|
|
||||||
// Error reading the object - requeue the request.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the found object and write the result back if there are any changes
|
|
||||||
if !reflect.DeepEqual(viewerRoleBinding.RoleRef, foundRoleBinding.RoleRef) {
|
|
||||||
log.Info("Deleting conflict workspace role binding", "workspace", instance.Name, "name", viewerRoleBinding.Name)
|
|
||||||
err = r.Delete(context.TODO(), foundRoleBinding)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return fmt.Errorf("conflict workspace role binding %s, waiting for recreate", foundRoleBinding.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ReconcileWorkspace) bindNamespaces(instance *tenantv1alpha1.Workspace) error {
|
|
||||||
|
|
||||||
nsList := &corev1.NamespaceList{}
|
|
||||||
options := client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: instance.Name})}
|
|
||||||
err := r.List(context.TODO(), nsList, &options)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err, fmt.Sprintf("list workspace %s namespace failed", instance.Name))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, namespace := range nsList.Items {
|
|
||||||
if !metav1.IsControlledBy(&namespace, instance) {
|
|
||||||
if err := controllerutil.SetControllerReference(instance, &namespace, r.scheme); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("Bind workspace", "namespace", namespace.Name, "workspace", instance.Name)
|
|
||||||
err = r.Update(context.TODO(), &namespace)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasSubject(subjects []rbac.Subject, user rbac.Subject) bool {
|
|
||||||
for _, subject := range subjects {
|
|
||||||
if reflect.DeepEqual(subject, user) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWorkspaceAdminRoleName(workspaceName string) string {
|
|
||||||
return fmt.Sprintf("workspace:%s:admin", workspaceName)
|
|
||||||
}
|
|
||||||
func getWorkspaceRegularRoleName(workspaceName string) string {
|
|
||||||
return fmt.Sprintf("workspace:%s:regular", workspaceName)
|
|
||||||
}
|
|
||||||
func getWorkspaceViewerRoleName(workspaceName string) string {
|
|
||||||
return fmt.Sprintf("workspace:%s:viewer", workspaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWorkspaceAdminRoleBindingName(workspaceName string) string {
|
|
||||||
return fmt.Sprintf("workspace:%s:admin", workspaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWorkspaceRegularRoleBindingName(workspaceName string) string {
|
|
||||||
return fmt.Sprintf("workspace:%s:regular", workspaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWorkspaceViewerRoleBindingName(workspaceName string) string {
|
|
||||||
return fmt.Sprintf("workspace:%s:viewer", workspaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWorkspaceAdmin(workspaceName string) *rbac.ClusterRole {
|
|
||||||
admin := &rbac.ClusterRole{}
|
|
||||||
admin.Name = getWorkspaceAdminRoleName(workspaceName)
|
|
||||||
admin.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName}
|
|
||||||
admin.Annotations = map[string]string{constants.DisplayNameAnnotationKey: constants.WorkspaceAdmin, constants.DescriptionAnnotationKey: workspaceAdminDescription, constants.CreatorAnnotationKey: constants.System}
|
|
||||||
admin.Rules = []rbac.PolicyRule{
|
|
||||||
{
|
|
||||||
Verbs: []string{"*"},
|
|
||||||
APIGroups: []string{"*"},
|
|
||||||
ResourceNames: []string{workspaceName},
|
|
||||||
Resources: []string{"workspaces", "workspaces/*"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"watch"},
|
|
||||||
APIGroups: []string{""},
|
|
||||||
Resources: []string{"namespaces"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"list"},
|
|
||||||
APIGroups: []string{"iam.kubesphere.io"},
|
|
||||||
Resources: []string{"users"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"get", "list"},
|
|
||||||
APIGroups: []string{"openpitrix.io"},
|
|
||||||
Resources: []string{"categories"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"*"},
|
|
||||||
APIGroups: []string{"openpitrix.io"},
|
|
||||||
Resources: []string{"applications", "apps", "apps/versions", "apps/events", "apps/action", "apps/audits", "repos", "repos/action", "attachments"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return admin
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWorkspaceRegular(workspaceName string) *rbac.ClusterRole {
|
|
||||||
regular := &rbac.ClusterRole{}
|
|
||||||
regular.Name = getWorkspaceRegularRoleName(workspaceName)
|
|
||||||
regular.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName}
|
|
||||||
regular.Annotations = map[string]string{constants.DisplayNameAnnotationKey: constants.WorkspaceRegular, constants.DescriptionAnnotationKey: workspaceRegularDescription, constants.CreatorAnnotationKey: constants.System}
|
|
||||||
regular.Rules = []rbac.PolicyRule{
|
|
||||||
{
|
|
||||||
Verbs: []string{"get"},
|
|
||||||
APIGroups: []string{"*"},
|
|
||||||
Resources: []string{"workspaces"},
|
|
||||||
ResourceNames: []string{workspaceName},
|
|
||||||
}, {
|
|
||||||
Verbs: []string{"create"},
|
|
||||||
APIGroups: []string{"tenant.kubesphere.io"},
|
|
||||||
Resources: []string{"workspaces/namespaces", "workspaces/devops"},
|
|
||||||
ResourceNames: []string{workspaceName},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"get"},
|
|
||||||
APIGroups: []string{"iam.kubesphere.io"},
|
|
||||||
ResourceNames: []string{workspaceName},
|
|
||||||
Resources: []string{"workspaces/members"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"get", "list"},
|
|
||||||
APIGroups: []string{"openpitrix.io"},
|
|
||||||
Resources: []string{"apps/events", "apps/action", "apps/audits", "categories"},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
Verbs: []string{"*"},
|
|
||||||
APIGroups: []string{"openpitrix.io"},
|
|
||||||
Resources: []string{"applications", "apps", "apps/versions", "repos", "repos/action", "attachments"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return regular
|
|
||||||
}
|
|
||||||
|
|
||||||
func getWorkspaceViewer(workspaceName string) *rbac.ClusterRole {
|
|
||||||
viewer := &rbac.ClusterRole{}
|
|
||||||
viewer.Name = getWorkspaceViewerRoleName(workspaceName)
|
|
||||||
viewer.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName}
|
|
||||||
viewer.Annotations = map[string]string{constants.DisplayNameAnnotationKey: constants.WorkspaceViewer, constants.DescriptionAnnotationKey: workspaceViewerDescription, constants.CreatorAnnotationKey: constants.System}
|
|
||||||
viewer.Rules = []rbac.PolicyRule{
|
|
||||||
{
|
|
||||||
Verbs: []string{"get", "list"},
|
|
||||||
APIGroups: []string{"*"},
|
|
||||||
ResourceNames: []string{workspaceName},
|
|
||||||
Resources: []string{"workspaces", "workspaces/*"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"watch"},
|
|
||||||
APIGroups: []string{""},
|
|
||||||
Resources: []string{"namespaces"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Verbs: []string{"get", "list"},
|
|
||||||
APIGroups: []string{"openpitrix.io"},
|
|
||||||
Resources: []string{"applications", "apps", "apps/events", "apps/action", "apps/audits", "apps/versions", "repos", "categories", "attachments"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return viewer
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func (f *informerFactories) Start(stopCh <-chan struct{}) {
|
|||||||
f.ksInformerFactory.Start(stopCh)
|
f.ksInformerFactory.Start(stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.informerFactory != nil {
|
if f.istioInformerFactory != nil {
|
||||||
f.istioInformerFactory.Start(stopCh)
|
f.istioInformerFactory.Start(stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package v1alpha2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/emicklei/go-restful"
|
"github.com/emicklei/go-restful"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
"kubesphere.io/kubesphere/pkg/models/iam/am"
|
"kubesphere.io/kubesphere/pkg/models/iam/am"
|
||||||
"kubesphere.io/kubesphere/pkg/models/iam/im"
|
"kubesphere.io/kubesphere/pkg/models/iam/im"
|
||||||
@@ -16,7 +16,7 @@ type iamHandler struct {
|
|||||||
imOperator im.IdentityManagementInterface
|
imOperator im.IdentityManagementInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIAMHandler(k8sClient k8s.Client, factory informers.InformerFactory, ldapClient ldappool.Interface, cacheClient cache.Interface, options *auth.AuthenticationOptions) *iamHandler {
|
func newIAMHandler(k8sClient k8s.Client, factory informers.InformerFactory, ldapClient ldappool.Interface, cacheClient cache.Interface, options *authoptions.AuthenticationOptions) *iamHandler {
|
||||||
return &iamHandler{
|
return &iamHandler{
|
||||||
amOperator: am.NewAMOperator(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory()),
|
amOperator: am.NewAMOperator(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory()),
|
||||||
imOperator: im.NewLDAPOperator(ldapClient),
|
imOperator: im.NewLDAPOperator(ldapClient),
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/emicklei/go-restful-openapi"
|
"github.com/emicklei/go-restful-openapi"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"kubesphere.io/kubesphere/pkg/api"
|
"kubesphere.io/kubesphere/pkg/api"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||||
"kubesphere.io/kubesphere/pkg/constants"
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
"kubesphere.io/kubesphere/pkg/informers"
|
"kubesphere.io/kubesphere/pkg/informers"
|
||||||
@@ -38,7 +38,7 @@ const groupName = "iam.kubesphere.io"
|
|||||||
|
|
||||||
var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha2"}
|
var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha2"}
|
||||||
|
|
||||||
func AddToContainer(c *restful.Container, k8sClient k8s.Client, factory informers.InformerFactory, ldapClient ldappool.Interface, cacheClient cache.Interface, options *auth.AuthenticationOptions) error {
|
func AddToContainer(c *restful.Container, k8sClient k8s.Client, factory informers.InformerFactory, ldapClient ldappool.Interface, cacheClient cache.Interface, options *authoptions.AuthenticationOptions) error {
|
||||||
ws := runtime.NewWebService(GroupVersion)
|
ws := runtime.NewWebService(GroupVersion)
|
||||||
|
|
||||||
handler := newIAMHandler(k8sClient, factory, ldapClient, cacheClient, options)
|
handler := newIAMHandler(k8sClient, factory, ldapClient, cacheClient, options)
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ import (
|
|||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"kubesphere.io/kubesphere/pkg/api"
|
"kubesphere.io/kubesphere/pkg/api"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
"kubesphere.io/kubesphere/pkg/api/auth"
|
||||||
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||||
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -33,11 +35,11 @@ import (
|
|||||||
|
|
||||||
type oauthHandler struct {
|
type oauthHandler struct {
|
||||||
issuer token.Issuer
|
issuer token.Issuer
|
||||||
config oauth.Configuration
|
options *authoptions.AuthenticationOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOAUTHHandler(issuer token.Issuer, config oauth.Configuration) *oauthHandler {
|
func newOAUTHHandler(issuer token.Issuer, options *authoptions.AuthenticationOptions) *oauthHandler {
|
||||||
return &oauthHandler{issuer: issuer, config: config}
|
return &oauthHandler{issuer: issuer, options: options}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement webhook authentication interface
|
// Implement webhook authentication interface
|
||||||
@@ -59,7 +61,7 @@ func (h *oauthHandler) TokenReviewHandler(req *restful.Request, resp *restful.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, _, err := h.issuer.Verify(tokenReview.Spec.Token)
|
user, err := h.issuer.Verify(tokenReview.Spec.Token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorln(err)
|
klog.Errorln(err)
|
||||||
@@ -82,8 +84,9 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
|
|||||||
user, ok := request.UserFrom(req.Request.Context())
|
user, ok := request.UserFrom(req.Request.Context())
|
||||||
clientId := req.QueryParameter("client_id")
|
clientId := req.QueryParameter("client_id")
|
||||||
responseType := req.QueryParameter("response_type")
|
responseType := req.QueryParameter("response_type")
|
||||||
|
redirectURI := req.QueryParameter("redirect_uri")
|
||||||
|
|
||||||
conf, err := h.config.Load(clientId)
|
conf, err := h.options.OAuthOptions.OAuthClient(clientId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||||
@@ -103,7 +106,13 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken, clm, err := h.issuer.IssueTo(user)
|
expiresIn := h.options.OAuthOptions.AccessTokenMaxAge
|
||||||
|
|
||||||
|
if conf.AccessTokenMaxAge != nil {
|
||||||
|
expiresIn = *conf.AccessTokenMaxAge
|
||||||
|
}
|
||||||
|
|
||||||
|
accessToken, err := h.issuer.IssueTo(user, expiresIn)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||||
@@ -111,11 +120,71 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectURL := fmt.Sprintf("%s?access_token=%s&token_type=Bearer", conf.RedirectURL, accessToken)
|
redirectURL, err := conf.ResolveRedirectURL(redirectURI)
|
||||||
expiresIn := clm.ExpiresAt - clm.IssuedAt
|
|
||||||
if expiresIn > 0 {
|
if err != nil {
|
||||||
redirectURL = fmt.Sprintf("%s&expires_in=%v", redirectURL, expiresIn)
|
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||||
|
resp.WriteError(http.StatusUnauthorized, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
redirectURL = fmt.Sprintf("%s#access_token=%s&token_type=Bearer", redirectURL, accessToken)
|
||||||
|
|
||||||
|
if expiresIn > 0 {
|
||||||
|
redirectURL = fmt.Sprintf("%s&expires_in=%v", redirectURL, expiresIn.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Header().Set("Content-Type", "text/plain")
|
||||||
http.Redirect(resp, req.Request, redirectURL, http.StatusFound)
|
http.Redirect(resp, req.Request, redirectURL, http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *oauthHandler) OAuthCallBackHandler(req *restful.Request, resp *restful.Response) {
|
||||||
|
|
||||||
|
code := req.QueryParameter("code")
|
||||||
|
name := req.PathParameter("callback")
|
||||||
|
|
||||||
|
if code == "" {
|
||||||
|
err := apierrors.NewUnauthorized("Unauthorized: missing code")
|
||||||
|
resp.WriteError(http.StatusUnauthorized, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
idP, err := h.options.OAuthOptions.IdentityProviderOptions(name)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||||
|
resp.WriteError(http.StatusUnauthorized, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
oauthIdentityProvider, err := identityprovider.ResolveOAuthProvider(idP.Type, idP.Provider)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||||
|
resp.WriteError(http.StatusUnauthorized, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := oauthIdentityProvider.IdentityExchange(code)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||||
|
resp.WriteError(http.StatusUnauthorized, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expiresIn := h.options.OAuthOptions.AccessTokenMaxAge
|
||||||
|
|
||||||
|
accessToken, err := h.issuer.IssueTo(user, expiresIn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||||
|
resp.WriteError(http.StatusUnauthorized, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result := oauth.Token{
|
||||||
|
AccessToken: accessToken,
|
||||||
|
TokenType: "Bearer",
|
||||||
|
ExpiresIn: int(expiresIn.Seconds()),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WriteEntity(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,38 +24,70 @@ import (
|
|||||||
"kubesphere.io/kubesphere/pkg/api"
|
"kubesphere.io/kubesphere/pkg/api"
|
||||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
"kubesphere.io/kubesphere/pkg/api/auth"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||||
|
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
||||||
"kubesphere.io/kubesphere/pkg/constants"
|
"kubesphere.io/kubesphere/pkg/constants"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddToContainer(c *restful.Container, issuer token.Issuer, configuration oauth.Configuration) error {
|
// ks-apiserver includes a built-in OAuth server. Users obtain OAuth access tokens to authenticate themselves to the API.
|
||||||
|
// The OAuth server supports standard authorization code grant and the implicit grant OAuth authorization flows.
|
||||||
|
// All requests for OAuth tokens involve a request to <ks-apiserver>/oauth/authorize.
|
||||||
|
// Most authentication integrations place an authenticating proxy in front of this endpoint, or configure ks-apiserver
|
||||||
|
// to validate credentials against a backing identity provider.
|
||||||
|
// Requests to <ks-apiserver>/oauth/authorize can come from user-agents that cannot display interactive login pages, such as the CLI.
|
||||||
|
func AddToContainer(c *restful.Container, issuer token.Issuer, options *authoptions.AuthenticationOptions) error {
|
||||||
ws := &restful.WebService{}
|
ws := &restful.WebService{}
|
||||||
ws.Path("/oauth").
|
ws.Path("/oauth").
|
||||||
Consumes(restful.MIME_JSON).
|
Consumes(restful.MIME_JSON).
|
||||||
Produces(restful.MIME_JSON)
|
Produces(restful.MIME_JSON)
|
||||||
|
|
||||||
handler := newOAUTHHandler(issuer, configuration)
|
handler := newOAUTHHandler(issuer, options)
|
||||||
|
|
||||||
// Implement webhook authentication interface
|
// Implement webhook authentication interface
|
||||||
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
|
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
|
||||||
ws.Route(ws.POST("/authenticate").
|
ws.Route(ws.POST("/authenticate").
|
||||||
Doc("TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver.").
|
Doc("TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be "+
|
||||||
|
"cached by the webhook token authenticator plugin in the kube-apiserver.").
|
||||||
Reads(auth.TokenReview{}).
|
Reads(auth.TokenReview{}).
|
||||||
To(handler.TokenReviewHandler).
|
To(handler.TokenReviewHandler).
|
||||||
Returns(http.StatusOK, api.StatusOK, auth.TokenReview{}).
|
Returns(http.StatusOK, api.StatusOK, auth.TokenReview{}).
|
||||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
|
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
|
||||||
|
|
||||||
// TODO Built-in oauth2 server (provider)
|
// Only support implicit grant flow
|
||||||
// web console use 'Resource Owner Password Credentials Grant' or 'Client Credentials Grant' request for an OAuth token
|
// https://tools.ietf.org/html/rfc6749#section-4.2
|
||||||
// https://tools.ietf.org/html/rfc6749#section-4.3
|
|
||||||
// https://tools.ietf.org/html/rfc6749#section-4.4
|
|
||||||
|
|
||||||
// curl -u admin:P@88w0rd 'http://ks-apiserver.kubesphere-system.svc/oauth/authorize?client_id=kubesphere-console-client&response_type=token' -v
|
|
||||||
ws.Route(ws.GET("/authorize").
|
ws.Route(ws.GET("/authorize").
|
||||||
|
Doc("All requests for OAuth tokens involve a request to <ks-apiserver>/oauth/authorize.").
|
||||||
|
Param(ws.QueryParameter("response_type", "The value MUST be one of \"code\" for requesting an "+
|
||||||
|
"authorization code as described by [RFC6749] Section 4.1.1, \"token\" for requesting an access token (implicit grant)"+
|
||||||
|
" as described by [RFC6749] Section 4.2.2.").Required(true)).
|
||||||
|
Param(ws.QueryParameter("client_id", "The client identifier issued to the client during the "+
|
||||||
|
"registration process described by [RFC6749] Section 2.2.").Required(true)).
|
||||||
|
Param(ws.QueryParameter("redirect_uri", "After completing its interaction with the resource owner, "+
|
||||||
|
"the authorization server directs the resource owner's user-agent back to the client.The redirection endpoint "+
|
||||||
|
"URI MUST be an absolute URI as defined by [RFC3986] Section 4.3.").Required(false)).
|
||||||
To(handler.AuthorizeHandler))
|
To(handler.AuthorizeHandler))
|
||||||
//ws.Route(ws.POST("/token"))
|
//ws.Route(ws.POST("/token"))
|
||||||
//ws.Route(ws.POST("/callback/{callback}"))
|
|
||||||
|
// Authorization callback URL, where the end of the URL contains the identity provider name.
|
||||||
|
// The provider name is also used to build the callback URL.
|
||||||
|
ws.Route(ws.GET("/callback/{callback}").
|
||||||
|
Doc("OAuth callback API, the path param callback is config by identity provider").
|
||||||
|
Param(ws.QueryParameter("access_token", "The access token issued by the authorization server.").
|
||||||
|
Required(true)).
|
||||||
|
Param(ws.QueryParameter("token_type", "The type of the token issued as described in [RFC6479] Section 7.1. "+
|
||||||
|
"Value is case insensitive.").Required(true)).
|
||||||
|
Param(ws.QueryParameter("expires_in", "The lifetime in seconds of the access token. For "+
|
||||||
|
"example, the value \"3600\" denotes that the access token will "+
|
||||||
|
"expire in one hour from the time the response was generated."+
|
||||||
|
"If omitted, the authorization server SHOULD provide the "+
|
||||||
|
"expiration time via other means or document the default value.")).
|
||||||
|
Param(ws.QueryParameter("scope", "if identical to the scope requested by the client;"+
|
||||||
|
"otherwise, REQUIRED. The scope of the access token as described by [RFC6479] Section 3.3.").Required(false)).
|
||||||
|
Param(ws.QueryParameter("state", "if the \"state\" parameter was present in the client authorization request."+
|
||||||
|
"The exact value received from the client.").Required(true)).
|
||||||
|
To(handler.OAuthCallBackHandler).
|
||||||
|
Returns(http.StatusOK, api.StatusOK, oauth.Token{}))
|
||||||
|
|
||||||
c.Add(ws)
|
c.Add(ws)
|
||||||
|
|
||||||
|
|||||||
51
pkg/kapis/serverconfig/v1alpha2/register.go
Normal file
51
pkg/kapis/serverconfig/v1alpha2/register.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2020 The KubeSphere Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* /
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1alpha2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emicklei/go-restful"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
|
||||||
|
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GroupName = "config.kubesphere.io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
|
||||||
|
|
||||||
|
func AddToContainer(c *restful.Container, config *apiserverconfig.Config) error {
|
||||||
|
webservice := runtime.NewWebService(GroupVersion)
|
||||||
|
|
||||||
|
webservice.Route(webservice.GET("/configs/oauth").
|
||||||
|
Doc("Information about the authorization server are published.").
|
||||||
|
To(func(request *restful.Request, response *restful.Response) {
|
||||||
|
response.WriteEntity(config.AuthenticationOptions.OAuthOptions)
|
||||||
|
}))
|
||||||
|
|
||||||
|
webservice.Route(webservice.GET("/configs/configz").
|
||||||
|
Doc("Information about the server configuration").
|
||||||
|
To(func(request *restful.Request, response *restful.Response) {
|
||||||
|
response.WriteAsJson(config.ToMap())
|
||||||
|
}))
|
||||||
|
|
||||||
|
c.Add(webservice)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -133,7 +133,9 @@ func NewFakeAMOperator() *FakeOperator {
|
|||||||
})
|
})
|
||||||
operator.saveFakeRole(platformRoleCacheKey(user.Anonymous), FakeRole{
|
operator.saveFakeRole(platformRoleCacheKey(user.Anonymous), FakeRole{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Rego: "package authz\ndefault allow = false",
|
Rego: `package authz
|
||||||
|
default allow = false
|
||||||
|
`,
|
||||||
})
|
})
|
||||||
return operator
|
return operator
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
digest "github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Image holds information about an image.
|
// Image holds information about an image.
|
||||||
|
|||||||
2
pkg/simple/client/cache/simple_cache.go
vendored
2
pkg/simple/client/cache/simple_cache.go
vendored
@@ -34,7 +34,7 @@ func (s *simpleCache) Keys(pattern string) ([]string, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var keys []string
|
var keys []string
|
||||||
for k, _ := range s.store {
|
for k := range s.store {
|
||||||
if re.MatchString(k) {
|
if re.MatchString(k) {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ func (b *Build) GetRevisionBranch() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Build) IsGood() bool {
|
func (b *Build) IsGood() bool {
|
||||||
return (!b.IsRunning() && b.Raw.Result == STATUS_SUCCESS)
|
return !b.IsRunning() && b.Raw.Result == STATUS_SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Build) IsRunning() bool {
|
func (b *Build) IsRunning() bool {
|
||||||
|
|||||||
@@ -98,6 +98,12 @@ func NewKubernetesClient(options *KubernetesOptions) (Client, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k.istio, err = istioclient.NewForConfig(config)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
k.master = options.Master
|
k.master = options.Master
|
||||||
k.config = config
|
k.config = config
|
||||||
|
|
||||||
|
|||||||
@@ -1,349 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 kubesphere
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"k8s.io/klog"
|
|
||||||
"kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
|
|
||||||
"kubesphere.io/kubesphere/pkg/constants"
|
|
||||||
"kubesphere.io/kubesphere/pkg/models"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Interface interface {
|
|
||||||
CreateGroup(group *models.Group) (*models.Group, error)
|
|
||||||
UpdateGroup(group *models.Group) (*models.Group, error)
|
|
||||||
DescribeGroup(name string) (*models.Group, error)
|
|
||||||
DeleteGroup(name string) error
|
|
||||||
ListUsers() (*models.PageableResponse, error)
|
|
||||||
ListWorkspaceDevOpsProjects(workspace string) (*v1alpha2.PageableDevOpsProject, error)
|
|
||||||
DeleteWorkspaceDevOpsProjects(workspace, devops string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
client *http.Client
|
|
||||||
|
|
||||||
apiServer string
|
|
||||||
accountServer string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewKubeSphereClient(options *Options) *Client {
|
|
||||||
return &Client{
|
|
||||||
client: &http.Client{},
|
|
||||||
apiServer: options.APIServer,
|
|
||||||
accountServer: options.AccountServer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
status int
|
|
||||||
message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Error() string {
|
|
||||||
return fmt.Sprintf("status: %d,message: %s", e.status, e.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) CreateGroup(group *models.Group) (*models.Group, error) {
|
|
||||||
data, err := json.Marshal(group)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups", c.accountServer), bytes.NewReader(data))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err = ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode > http.StatusOK {
|
|
||||||
return nil, Error{resp.StatusCode, string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(data, group)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return group, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) UpdateGroup(group *models.Group) (*models.Group, error) {
|
|
||||||
data, err := json.Marshal(group)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups/%s", c.accountServer, group.Name), bytes.NewReader(data))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err = ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode > http.StatusOK {
|
|
||||||
return nil, Error{resp.StatusCode, string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(data, group)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return group, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) DeleteGroup(name string) error {
|
|
||||||
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups/%s", c.accountServer, name), nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode > http.StatusOK {
|
|
||||||
return Error{resp.StatusCode, string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) DescribeGroup(name string) (*models.Group, error) {
|
|
||||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups/%s", c.accountServer, name), nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode > http.StatusOK {
|
|
||||||
return nil, Error{resp.StatusCode, string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
var group models.Group
|
|
||||||
err = json.Unmarshal(data, &group)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &group, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) ListUsers() (*models.PageableResponse, error) {
|
|
||||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/users", c.accountServer), nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Add("Authorization", c.accountServer)
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode > http.StatusOK {
|
|
||||||
return nil, Error{resp.StatusCode, string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result models.PageableResponse
|
|
||||||
err = json.Unmarshal(data, &result)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) ListWorkspaceDevOpsProjects(workspace string) (*v1alpha2.PageableDevOpsProject, error) {
|
|
||||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/kapis/tenant.kubesphere.io/v1alpha2/workspaces/%s/devops", c.apiServer, workspace), nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add(constants.UserNameHeader, constants.AdminUserName)
|
|
||||||
|
|
||||||
klog.Info(req.Method, req.URL)
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode > http.StatusOK {
|
|
||||||
klog.Error(req.Method, req.URL, resp.StatusCode, string(data))
|
|
||||||
return nil, Error{resp.StatusCode, string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result v1alpha2.PageableDevOpsProject
|
|
||||||
err = json.Unmarshal(data, &result)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &result, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) DeleteWorkspaceDevOpsProjects(workspace, devops string) error {
|
|
||||||
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/kapis/tenant.kubesphere.io/v1alpha2/workspaces/%s/devops/%s", c.apiServer, workspace, devops), nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Header.Add(constants.UserNameHeader, constants.AdminUserName)
|
|
||||||
|
|
||||||
klog.Info(req.Method, req.URL)
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
klog.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resp.StatusCode > http.StatusOK {
|
|
||||||
klog.Error(req.Method, req.URL, resp.StatusCode, string(data))
|
|
||||||
return Error{resp.StatusCode, string(data)}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsNotFound(err error) bool {
|
|
||||||
if e, ok := err.(Error); ok {
|
|
||||||
if e.status == http.StatusNotFound {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.Contains(e.message, "not exist") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.Contains(e.message, "not found") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsExist(err error) bool {
|
|
||||||
if e, ok := err.(Error); ok {
|
|
||||||
if e.status == http.StatusConflict {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.Contains(e.message, "Already Exists") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package kubesphere
|
|
||||||
|
|
||||||
import "github.com/spf13/pflag"
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
APIServer string `json:"apiServer" yaml:"apiServer"`
|
|
||||||
AccountServer string `json:"accountServer" yaml:"accountServer"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKubeSphereOptions create a default options
|
|
||||||
func NewKubeSphereOptions() *Options {
|
|
||||||
return &Options{
|
|
||||||
APIServer: "http://ks-apiserver.kubesphere-system.svc",
|
|
||||||
AccountServer: "http://ks-account.kubesphere-system.svc",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Options) ApplyTo(options *Options) {
|
|
||||||
if s.AccountServer != "" {
|
|
||||||
options.AccountServer = s.AccountServer
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.APIServer != "" {
|
|
||||||
options.APIServer = s.APIServer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Options) Validate() []error {
|
|
||||||
errs := []error{}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Options) AddFlags(fs *pflag.FlagSet) {
|
|
||||||
fs.StringVar(&s.APIServer, "kubesphere-apiserver-host", s.APIServer, ""+
|
|
||||||
"KubeSphere apiserver host address.")
|
|
||||||
|
|
||||||
fs.StringVar(&s.AccountServer, "kubesphere-account-host", s.AccountServer, ""+
|
|
||||||
"KubeSphere account server host address.")
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package mysql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
reflectutils "kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user