Merge pull request #1971 from wansir/dev
implement identity provider and built-in oauth server
This commit is contained in:
@@ -25,6 +25,7 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/controller/s2ibinary"
|
||||
"kubesphere.io/kubesphere/pkg/controller/s2irun"
|
||||
"kubesphere.io/kubesphere/pkg/controller/storage/expansion"
|
||||
"kubesphere.io/kubesphere/pkg/controller/user"
|
||||
"kubesphere.io/kubesphere/pkg/controller/virtualservice"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
@@ -87,6 +88,11 @@ func AddControllers(
|
||||
kubernetesInformer.Apps().V1().ReplicaSets(),
|
||||
kubernetesInformer.Apps().V1().StatefulSets())
|
||||
|
||||
userController := user.NewController(
|
||||
client.Kubernetes(),
|
||||
client.KubeSphere(),
|
||||
kubesphereInformer.Iam().V1alpha2().Users())
|
||||
|
||||
controllers := map[string]manager.Runnable{
|
||||
"virtualservice-controller": vsController,
|
||||
"destinationrule-controller": drController,
|
||||
@@ -95,6 +101,7 @@ func AddControllers(
|
||||
"s2ibinary-controller": s2iBinaryController,
|
||||
"s2irun-controller": s2iRunController,
|
||||
"volumeexpansion-controller": volumeExpansionController,
|
||||
"user-controller": userController,
|
||||
}
|
||||
|
||||
for name, ctrl := range controllers {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
kubesphereconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||
"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/s3"
|
||||
"strings"
|
||||
@@ -21,9 +20,7 @@ type KubeSphereControllerManagerOptions struct {
|
||||
DevopsOptions *jenkins.Options
|
||||
S3Options *s3.Options
|
||||
OpenPitrixOptions *openpitrix.Options
|
||||
KubeSphereOptions *kubesphere.Options
|
||||
|
||||
LeaderElection *leaderelection.LeaderElectionConfig
|
||||
LeaderElection *leaderelection.LeaderElectionConfig
|
||||
}
|
||||
|
||||
func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions {
|
||||
@@ -32,7 +29,6 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions
|
||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||
S3Options: s3.NewS3Options(),
|
||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||
KubeSphereOptions: kubesphere.NewKubeSphereOptions(),
|
||||
LeaderElection: &leaderelection.LeaderElectionConfig{
|
||||
LeaseDuration: 30 * time.Second,
|
||||
RenewDeadline: 15 * time.Second,
|
||||
|
||||
@@ -38,7 +38,6 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/controller/workspace"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"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/utils/term"
|
||||
"os"
|
||||
@@ -50,11 +49,13 @@ func NewControllerManagerCommand() *cobra.Command {
|
||||
s := options.NewKubeSphereControllerManagerOptions()
|
||||
conf, err := controllerconfig.TryLoadFromDisk()
|
||||
if err == nil {
|
||||
// make sure LeaderElection is not nil
|
||||
s = &options.KubeSphereControllerManagerOptions{
|
||||
KubernetesOptions: conf.KubernetesOptions,
|
||||
DevopsOptions: conf.DevopsOptions,
|
||||
S3Options: conf.S3Options,
|
||||
OpenPitrixOptions: conf.OpenPitrixOptions,
|
||||
LeaderElection: s.LeaderElection,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +97,6 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
||||
return err
|
||||
}
|
||||
|
||||
kubesphereClient := kclient.NewKubeSphereClient(s.KubeSphereOptions)
|
||||
|
||||
openpitrixClient, err := openpitrix.NewClient(s.OpenPitrixOptions)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to create openpitrix client %v", err)
|
||||
@@ -121,7 +120,6 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
||||
*/
|
||||
|
||||
informerFactory := informers.NewInformerFactories(kubernetesClient.Kubernetes(), kubernetesClient.KubeSphere(), kubernetesClient.Istio(), kubernetesClient.Application())
|
||||
informerFactory.Start(stopCh)
|
||||
|
||||
run := func(ctx context.Context) {
|
||||
klog.V(0).Info("setting up manager")
|
||||
@@ -136,7 +134,7 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
||||
}
|
||||
|
||||
klog.V(0).Info("Setting up controllers")
|
||||
err = workspace.Add(mgr, kubesphereClient)
|
||||
err = workspace.Add(mgr)
|
||||
if err != nil {
|
||||
klog.Fatal("Unable to create workspace controller")
|
||||
}
|
||||
@@ -150,6 +148,9 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
|
||||
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.")
|
||||
if err = mgr.Start(stopCh); err != nil {
|
||||
klog.Fatalf("unable to run the manager: %v", err)
|
||||
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"fmt"
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
||||
"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"
|
||||
genericoptions "kubesphere.io/kubesphere/pkg/server/options"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
@@ -29,42 +30,32 @@ import (
|
||||
type ServerRunOptions struct {
|
||||
ConfigFile string
|
||||
GenericServerRunOptions *genericoptions.ServerRunOptions
|
||||
KubernetesOptions *k8s.KubernetesOptions
|
||||
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
|
||||
*apiserverconfig.Config
|
||||
|
||||
//
|
||||
DebugMode bool
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
|
||||
s := ServerRunOptions{
|
||||
s := &ServerRunOptions{
|
||||
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
|
||||
KubernetesOptions: k8s.NewKubernetesOptions(),
|
||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||
SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
|
||||
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),
|
||||
MySQLOptions: mysql.NewMySQLOptions(),
|
||||
MonitoringOptions: prometheus.NewPrometheusOptions(),
|
||||
S3Options: s3.NewS3Options(),
|
||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||
LoggingOptions: esclient.NewElasticSearchOptions(),
|
||||
LdapOptions: ldap.NewOptions(),
|
||||
CacheOptions: cache.NewRedisOptions(),
|
||||
AuthenticateOptions: auth.NewAuthenticateOptions(),
|
||||
Config: &apiserverconfig.Config{
|
||||
KubernetesOptions: k8s.NewKubernetesOptions(),
|
||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||
SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
|
||||
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),
|
||||
MySQLOptions: mysql.NewMySQLOptions(),
|
||||
MonitoringOptions: prometheus.NewPrometheusOptions(),
|
||||
S3Options: s3.NewS3Options(),
|
||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||
LoggingOptions: esclient.NewElasticSearchOptions(),
|
||||
LdapOptions: ldap.NewOptions(),
|
||||
RedisOptions: cache.NewRedisOptions(),
|
||||
AuthenticationOptions: authoptions.NewAuthenticateOptions(),
|
||||
},
|
||||
}
|
||||
|
||||
return &s
|
||||
return s
|
||||
}
|
||||
|
||||
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.")
|
||||
s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions)
|
||||
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.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
|
||||
s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions)
|
||||
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.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
|
||||
s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh"), s.ServiceMeshOptions)
|
||||
@@ -100,7 +91,7 @@ const fakeInterface string = "FAKE"
|
||||
// NewAPIServer creates an APIServer instance using given options
|
||||
func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIServer, error) {
|
||||
apiServer := &apiserver.APIServer{
|
||||
AuthenticateOptions: s.AuthenticateOptions,
|
||||
Config: s.Config,
|
||||
}
|
||||
|
||||
kubernetesClient, err := k8s.NewKubernetesClient(s.KubernetesOptions)
|
||||
@@ -156,11 +147,11 @@ func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIS
|
||||
}
|
||||
|
||||
var cacheClient cache.Interface
|
||||
if s.CacheOptions.Host != "" {
|
||||
if s.CacheOptions.Host == fakeInterface && s.DebugMode {
|
||||
if s.RedisOptions.Host != "" {
|
||||
if s.RedisOptions.Host == fakeInterface && s.DebugMode {
|
||||
apiServer.CacheClient = cache.NewSimpleCache()
|
||||
} else {
|
||||
cacheClient, err = cache.NewRedisClient(s.CacheOptions, stopCh)
|
||||
cacheClient, err = cache.NewRedisClient(s.RedisOptions, stopCh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -37,18 +37,7 @@ func NewAPIServerCommand() *cobra.Command {
|
||||
if err == nil {
|
||||
s = &options.ServerRunOptions{
|
||||
GenericServerRunOptions: s.GenericServerRunOptions,
|
||||
KubernetesOptions: conf.KubernetesOptions,
|
||||
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,
|
||||
Config: conf,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
set -e
|
||||
|
||||
GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 devops:v1alpha1 tower:v1alpha1"
|
||||
GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 devops:v1alpha1 iam:v1alpha2 tower:v1alpha1"
|
||||
|
||||
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"
|
||||
|
||||
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"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic"
|
||||
"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/basictoken"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/path"
|
||||
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/filters"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2"
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2"
|
||||
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2"
|
||||
monitoringv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha2"
|
||||
@@ -35,8 +33,8 @@ import (
|
||||
operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2"
|
||||
resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2"
|
||||
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha3"
|
||||
"kubesphere.io/kubesphere/pkg/kapis/serverconfig/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"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam/am"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam/im"
|
||||
@@ -75,7 +73,7 @@ type APIServer struct {
|
||||
//
|
||||
Server *http.Server
|
||||
|
||||
AuthenticateOptions *auth.AuthenticationOptions
|
||||
Config *apiserverconfig.Config
|
||||
|
||||
// webservice container, where all webservice defines
|
||||
container *restful.Container
|
||||
@@ -135,19 +133,19 @@ func (s *APIServer) PrepareRun() error {
|
||||
}
|
||||
|
||||
func (s *APIServer) installKubeSphereAPIs() {
|
||||
|
||||
urlruntime.Must(v1alpha2.AddToContainer(s.container, s.Config))
|
||||
urlruntime.Must(resourcev1alpha3.AddToContainer(s.container, s.InformerFactory))
|
||||
// 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(monitoringv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.MonitoringClient))
|
||||
urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient))
|
||||
urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes()))
|
||||
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(iamv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory, s.LdapClient, s.CacheClient, s.AuthenticateOptions))
|
||||
urlruntime.Must(oauth.AddToContainer(s.container, token.NewJwtTokenIssuer(token.DefaultIssuerName, s.AuthenticateOptions, s.CacheClient), &oauth2.SimpleConfigManager{}))
|
||||
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.Config.AuthenticationOptions, s.CacheClient), s.Config.AuthenticationOptions))
|
||||
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container))
|
||||
}
|
||||
|
||||
@@ -187,7 +185,7 @@ func (s *APIServer) buildHandlerChain() {
|
||||
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
|
||||
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)
|
||||
authorizer := unionauthorizer.New(pathAuthorizer,
|
||||
authorizerfactory.NewOPAAuthorizer(am.NewFakeAMOperator()))
|
||||
@@ -196,7 +194,7 @@ func (s *APIServer) buildHandlerChain() {
|
||||
authn := unionauth.New(anonymous.NewAuthenticator(),
|
||||
basictoken.New(basic.NewBasicAuthenticator(im.NewFakeOperator())),
|
||||
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.WithRequestInfo(handler, requestInfoResolver)
|
||||
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) {
|
||||
providedUser, _, err := t.jwtTokenIssuer.Verify(token)
|
||||
providedUser, err := t.jwtTokenIssuer.Verify(token)
|
||||
if err != nil {
|
||||
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 (
|
||||
"fmt"
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -32,15 +33,13 @@ type AuthenticationOptions struct {
|
||||
// maximum retries when authenticate failed
|
||||
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
|
||||
MultipleLogin bool `json:"multipleLogin" yaml:"multipleLogin"`
|
||||
|
||||
// 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 {
|
||||
@@ -48,7 +47,7 @@ func NewAuthenticateOptions() *AuthenticationOptions {
|
||||
AuthenticateRateLimiterMaxTries: 5,
|
||||
AuthenticateRateLimiterDuration: time.Minute * 30,
|
||||
MaxAuthenticateRetries: 0,
|
||||
TokenExpiration: 0,
|
||||
OAuthOptions: oauth.NewOptions(),
|
||||
MultipleLogin: false,
|
||||
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.DurationVar(&options.AuthenticateRateLimiterDuration, "authenticate-rate-limiter-duration", s.AuthenticateRateLimiterDuration, "")
|
||||
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.StringVar(&options.JwtSecret, "jwt-secret", s.JwtSecret, "Secret to sign jwt token, must not be empty.")
|
||||
}
|
||||
@@ -18,13 +18,15 @@
|
||||
|
||||
package token
|
||||
|
||||
import "time"
|
||||
|
||||
// Issuer issues token to user, tokens are required to perform mutating requests to resources
|
||||
type Issuer interface {
|
||||
// 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(string) (User, *Claims, error)
|
||||
Verify(string) (User, error)
|
||||
|
||||
// Revoke a token,
|
||||
Revoke(token string) error
|
||||
|
||||
@@ -21,8 +21,8 @@ package token
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
||||
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
"time"
|
||||
@@ -44,35 +44,35 @@ type Claims struct {
|
||||
|
||||
type jwtTokenIssuer struct {
|
||||
name string
|
||||
options *auth.AuthenticationOptions
|
||||
options *authoptions.AuthenticationOptions
|
||||
cache cache.Interface
|
||||
keyFunc jwt.Keyfunc
|
||||
}
|
||||
|
||||
func (s *jwtTokenIssuer) Verify(tokenString string) (User, *Claims, error) {
|
||||
func (s *jwtTokenIssuer) Verify(tokenString string) (User, error) {
|
||||
if len(tokenString) == 0 {
|
||||
return nil, nil, errInvalidToken
|
||||
return nil, errInvalidToken
|
||||
}
|
||||
_, err := s.cache.Get(tokenCacheKey(tokenString))
|
||||
|
||||
if err != nil {
|
||||
if err == cache.ErrNoSuchKey {
|
||||
return nil, nil, errTokenExpired
|
||||
return nil, errTokenExpired
|
||||
}
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clm := &Claims{}
|
||||
|
||||
_, err = jwt.ParseWithClaims(tokenString, clm, s.keyFunc)
|
||||
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{
|
||||
Username: user.GetName(),
|
||||
UID: user.GetUID(),
|
||||
@@ -83,8 +83,8 @@ func (s *jwtTokenIssuer) IssueTo(user User) (string, *Claims, error) {
|
||||
},
|
||||
}
|
||||
|
||||
if s.options.TokenExpiration > 0 {
|
||||
clm.ExpiresAt = clm.IssuedAt + int64(s.options.TokenExpiration.Seconds())
|
||||
if expiresIn > 0 {
|
||||
clm.ExpiresAt = clm.IssuedAt + int64(expiresIn.Seconds())
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
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 {
|
||||
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{
|
||||
name: issuerName,
|
||||
options: options,
|
||||
|
||||
@@ -20,14 +20,14 @@ package token
|
||||
|
||||
import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
||||
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJwtTokenIssuer(t *testing.T) {
|
||||
options := auth.NewAuthenticateOptions()
|
||||
options := authoptions.NewAuthenticateOptions()
|
||||
options.JwtSecret = "kubesphere"
|
||||
issuer := NewJwtTokenIssuer(DefaultIssuerName, options, cache.NewSimpleCache())
|
||||
|
||||
@@ -54,12 +54,12 @@ func TestJwtTokenIssuer(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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, _, err := issuer.Verify(token)
|
||||
got, err := issuer.Verify(token)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@ import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/spf13/viper"
|
||||
"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/simple/client/alerting"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||
"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/logging/elasticsearch"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
|
||||
@@ -63,23 +62,18 @@ const (
|
||||
|
||||
// Config defines everything needed for apiserver to deal with external services
|
||||
type Config struct {
|
||||
MySQLOptions *mysql.Options `json:"mysql,omitempty" yaml:"mysql,omitempty" mapstructure:"mysql"`
|
||||
DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"`
|
||||
SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"`
|
||||
KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"`
|
||||
ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"`
|
||||
LdapOptions *ldap.Options `json:"ldap,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"`
|
||||
RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"`
|
||||
S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"`
|
||||
OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"`
|
||||
MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"`
|
||||
LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"`
|
||||
|
||||
// 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"`
|
||||
|
||||
MySQLOptions *mysql.Options `json:"mysql,omitempty" yaml:"mysql,omitempty" mapstructure:"mysql"`
|
||||
DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"`
|
||||
SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"`
|
||||
KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"`
|
||||
ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"`
|
||||
LdapOptions *ldap.Options `json:"ldap,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"`
|
||||
RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"`
|
||||
S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"`
|
||||
OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"`
|
||||
MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"`
|
||||
LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"`
|
||||
AuthenticationOptions *authoptions.AuthenticationOptions `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"`
|
||||
// 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
|
||||
AlertingOptions *alerting.Options `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"`
|
||||
@@ -89,21 +83,20 @@ type Config struct {
|
||||
// newConfig creates a default non-empty Config
|
||||
func New() *Config {
|
||||
return &Config{
|
||||
MySQLOptions: mysql.NewMySQLOptions(),
|
||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||
SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
|
||||
KubernetesOptions: k8s.NewKubernetesOptions(),
|
||||
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),
|
||||
LdapOptions: ldap.NewOptions(),
|
||||
RedisOptions: cache.NewRedisOptions(),
|
||||
S3Options: s3.NewS3Options(),
|
||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||
MonitoringOptions: prometheus.NewPrometheusOptions(),
|
||||
KubeSphereOptions: kubesphere.NewKubeSphereOptions(),
|
||||
AlertingOptions: alerting.NewAlertingOptions(),
|
||||
NotificationOptions: notification.NewNotificationOptions(),
|
||||
LoggingOptions: elasticsearch.NewElasticSearchOptions(),
|
||||
AuthenticateOptions: auth.NewAuthenticateOptions(),
|
||||
MySQLOptions: mysql.NewMySQLOptions(),
|
||||
DevopsOptions: jenkins.NewDevopsOptions(),
|
||||
SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
|
||||
KubernetesOptions: k8s.NewKubernetesOptions(),
|
||||
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),
|
||||
LdapOptions: ldap.NewOptions(),
|
||||
RedisOptions: cache.NewRedisOptions(),
|
||||
S3Options: s3.NewS3Options(),
|
||||
OpenPitrixOptions: openpitrix.NewOptions(),
|
||||
MonitoringOptions: prometheus.NewPrometheusOptions(),
|
||||
AlertingOptions: alerting.NewAlertingOptions(),
|
||||
NotificationOptions: notification.NewNotificationOptions(),
|
||||
LoggingOptions: elasticsearch.NewElasticSearchOptions(),
|
||||
AuthenticationOptions: authoptions.NewAuthenticateOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,17 +118,9 @@ func TryLoadFromDisk() (*Config, error) {
|
||||
}
|
||||
|
||||
conf := New()
|
||||
|
||||
if err := viper.Unmarshal(conf); err != nil {
|
||||
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
|
||||
@@ -151,7 +136,7 @@ func (conf *Config) InstallAPI(c *restful.Container) {
|
||||
ws.Route(ws.GET("/configz").
|
||||
To(func(request *restful.Request, response *restful.Response) {
|
||||
conf.stripEmptyOptions()
|
||||
response.WriteAsJson(conf.toMap())
|
||||
response.WriteAsJson(conf.ToMap())
|
||||
}).
|
||||
Doc("Get system components configuration").
|
||||
Produces(restful.MIME_JSON).
|
||||
@@ -163,7 +148,8 @@ func (conf *Config) InstallAPI(c *restful.Container) {
|
||||
|
||||
// convertToMap simply converts config to map[string]bool
|
||||
// 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)
|
||||
|
||||
if conf == nil {
|
||||
|
||||
@@ -2,14 +2,15 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gopkg.in/yaml.v2"
|
||||
"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/cache"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
|
||||
"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/logging/elasticsearch"
|
||||
"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/servicemesh"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newTestConfig() *Config {
|
||||
conf := &Config{
|
||||
func newTestConfig() (*Config, error) {
|
||||
|
||||
var conf = &Config{
|
||||
MySQLOptions: &mysql.Options{
|
||||
Host: "10.68.96.5:3306",
|
||||
Username: "root",
|
||||
@@ -96,25 +97,34 @@ func newTestConfig() *Config {
|
||||
IndexPrefix: "elk",
|
||||
Version: "6",
|
||||
},
|
||||
KubeSphereOptions: &kubesphere.Options{
|
||||
APIServer: "http://ks-apiserver.kubesphere-system.svc",
|
||||
AccountServer: "http://ks-account.kubesphere-system.svc",
|
||||
},
|
||||
AlertingOptions: &alerting.Options{
|
||||
Endpoint: "http://alerting.kubesphere-alerting-system.svc:9200",
|
||||
},
|
||||
NotificationOptions: ¬ification.Options{
|
||||
Endpoint: "http://notification.kubesphere-alerting-system.svc:9200",
|
||||
},
|
||||
AuthenticateOptions: &auth.AuthenticationOptions{
|
||||
AuthenticationOptions: &authoptions.AuthenticationOptions{
|
||||
AuthenticateRateLimiterMaxTries: 5,
|
||||
AuthenticateRateLimiterDuration: 30 * time.Minute,
|
||||
MaxAuthenticateRetries: 6,
|
||||
TokenExpiration: 30 * time.Minute,
|
||||
JwtSecret: "xxxxxx",
|
||||
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) {
|
||||
@@ -122,7 +132,6 @@ func saveTestConfig(t *testing.T, conf *Config) {
|
||||
if err != nil {
|
||||
t.Fatalf("error marshal config. %v", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(fmt.Sprintf("%s.yaml", defaultConfigurationName), content, 0640)
|
||||
if err != nil {
|
||||
t.Fatalf("error write configuration file, %v", err)
|
||||
@@ -144,7 +153,10 @@ func cleanTestConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
conf := newTestConfig()
|
||||
conf, err := newTestConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
saveTestConfig(t, conf)
|
||||
defer cleanTestConfig(t)
|
||||
|
||||
@@ -152,48 +164,7 @@ func TestGet(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := reflectutils.Equal(conf, conf2); diff != nil {
|
||||
if diff := cmp.Diff(conf, conf2); 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
rest "k8s.io/client-go/rest"
|
||||
flowcontrol "k8s.io/client-go/util/flowcontrol"
|
||||
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1"
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2"
|
||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1"
|
||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
|
||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1"
|
||||
@@ -34,6 +35,7 @@ import (
|
||||
type Interface interface {
|
||||
Discovery() discovery.DiscoveryInterface
|
||||
DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface
|
||||
IamV1alpha2() iamv1alpha2.IamV1alpha2Interface
|
||||
NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface
|
||||
ServicemeshV1alpha2() servicemeshv1alpha2.ServicemeshV1alpha2Interface
|
||||
TenantV1alpha1() tenantv1alpha1.TenantV1alpha1Interface
|
||||
@@ -45,6 +47,7 @@ type Interface interface {
|
||||
type Clientset struct {
|
||||
*discovery.DiscoveryClient
|
||||
devopsV1alpha1 *devopsv1alpha1.DevopsV1alpha1Client
|
||||
iamV1alpha2 *iamv1alpha2.IamV1alpha2Client
|
||||
networkV1alpha1 *networkv1alpha1.NetworkV1alpha1Client
|
||||
servicemeshV1alpha2 *servicemeshv1alpha2.ServicemeshV1alpha2Client
|
||||
tenantV1alpha1 *tenantv1alpha1.TenantV1alpha1Client
|
||||
@@ -56,6 +59,11 @@ func (c *Clientset) DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface {
|
||||
return c.devopsV1alpha1
|
||||
}
|
||||
|
||||
// IamV1alpha2 retrieves the IamV1alpha2Client
|
||||
func (c *Clientset) IamV1alpha2() iamv1alpha2.IamV1alpha2Interface {
|
||||
return c.iamV1alpha2
|
||||
}
|
||||
|
||||
// NetworkV1alpha1 retrieves the NetworkV1alpha1Client
|
||||
func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface {
|
||||
return c.networkV1alpha1
|
||||
@@ -101,6 +109,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.iamV1alpha2, err = iamv1alpha2.NewForConfig(&configShallowCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.networkV1alpha1, err = networkv1alpha1.NewForConfig(&configShallowCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -130,6 +142,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
|
||||
func NewForConfigOrDie(c *rest.Config) *Clientset {
|
||||
var cs Clientset
|
||||
cs.devopsV1alpha1 = devopsv1alpha1.NewForConfigOrDie(c)
|
||||
cs.iamV1alpha2 = iamv1alpha2.NewForConfigOrDie(c)
|
||||
cs.networkV1alpha1 = networkv1alpha1.NewForConfigOrDie(c)
|
||||
cs.servicemeshV1alpha2 = servicemeshv1alpha2.NewForConfigOrDie(c)
|
||||
cs.tenantV1alpha1 = tenantv1alpha1.NewForConfigOrDie(c)
|
||||
@@ -143,6 +156,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
|
||||
func New(c rest.Interface) *Clientset {
|
||||
var cs Clientset
|
||||
cs.devopsV1alpha1 = devopsv1alpha1.New(c)
|
||||
cs.iamV1alpha2 = iamv1alpha2.New(c)
|
||||
cs.networkV1alpha1 = networkv1alpha1.New(c)
|
||||
cs.servicemeshV1alpha2 = servicemeshv1alpha2.New(c)
|
||||
cs.tenantV1alpha1 = tenantv1alpha1.New(c)
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
clientset "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1"
|
||||
fakedevopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1/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"
|
||||
fakenetworkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1/fake"
|
||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
|
||||
@@ -89,6 +91,11 @@ func (c *Clientset) DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface {
|
||||
return &fakedevopsv1alpha1.FakeDevopsV1alpha1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// IamV1alpha2 retrieves the IamV1alpha2Client
|
||||
func (c *Clientset) IamV1alpha2() iamv1alpha2.IamV1alpha2Interface {
|
||||
return &fakeiamv1alpha2.FakeIamV1alpha2{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// NetworkV1alpha1 retrieves the NetworkV1alpha1Client
|
||||
func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface {
|
||||
return &fakenetworkv1alpha1.FakeNetworkV1alpha1{Fake: &c.Fake}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
@@ -36,6 +37,7 @@ var codecs = serializer.NewCodecFactory(scheme)
|
||||
var parameterCodec = runtime.NewParameterCodec(scheme)
|
||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||
devopsv1alpha1.AddToScheme,
|
||||
iamv1alpha2.AddToScheme,
|
||||
networkv1alpha1.AddToScheme,
|
||||
servicemeshv1alpha2.AddToScheme,
|
||||
tenantv1alpha1.AddToScheme,
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
@@ -36,6 +37,7 @@ var Codecs = serializer.NewCodecFactory(Scheme)
|
||||
var ParameterCodec = runtime.NewParameterCodec(Scheme)
|
||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||
devopsv1alpha1.AddToScheme,
|
||||
iamv1alpha2.AddToScheme,
|
||||
networkv1alpha1.AddToScheme,
|
||||
servicemeshv1alpha2.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"
|
||||
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||
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"
|
||||
network "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network"
|
||||
servicemesh "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh"
|
||||
@@ -177,6 +178,7 @@ type SharedInformerFactory interface {
|
||||
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
|
||||
|
||||
Devops() devops.Interface
|
||||
Iam() iam.Interface
|
||||
Network() network.Interface
|
||||
Servicemesh() servicemesh.Interface
|
||||
Tenant() tenant.Interface
|
||||
@@ -187,6 +189,10 @@ func (f *sharedInformerFactory) Devops() devops.Interface {
|
||||
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 {
|
||||
return network.New(f, f.namespace, f.tweakListOptions)
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ import (
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
cache "k8s.io/client-go/tools/cache"
|
||||
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||
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"
|
||||
towerv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tower/v1alpha1"
|
||||
)
|
||||
@@ -66,6 +67,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
||||
case v1alpha1.SchemeGroupVersion.WithResource("s2iruns"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iRuns().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
|
||||
case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().NamespaceNetworkPolicies().Informer()}, nil
|
||||
@@ -73,9 +78,9 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().WorkspaceNetworkPolicies().Informer()}, nil
|
||||
|
||||
// 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
|
||||
case v1alpha2.SchemeGroupVersion.WithResource("strategies"):
|
||||
case servicemeshv1alpha2.SchemeGroupVersion.WithResource("strategies"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().Strategies().Informer()}, nil
|
||||
|
||||
// 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, 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"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -44,22 +43,6 @@ import (
|
||||
"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
|
||||
* 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
|
||||
}
|
||||
|
||||
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 {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
@@ -357,24 +327,3 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error {
|
||||
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 (
|
||||
"context"
|
||||
"fmt"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/tools/record"
|
||||
log "k8s.io/klog"
|
||||
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"
|
||||
"reflect"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
"sync"
|
||||
)
|
||||
|
||||
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
|
||||
// and Start it when the Manager is Started.
|
||||
func Add(mgr manager.Manager, kubesphereClient kubesphere.Interface) error {
|
||||
return add(mgr, newReconciler(mgr, kubesphereClient))
|
||||
func Add(mgr manager.Manager) error {
|
||||
return add(mgr, newReconciler(mgr))
|
||||
}
|
||||
|
||||
// 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(),
|
||||
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
|
||||
@@ -88,7 +75,6 @@ type ReconcileWorkspace struct {
|
||||
client.Client
|
||||
scheme *runtime.Scheme
|
||||
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
|
||||
@@ -125,9 +111,6 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res
|
||||
// The object is being deleted
|
||||
if sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if f.informerFactory != nil {
|
||||
if f.istioInformerFactory != nil {
|
||||
f.istioInformerFactory.Start(stopCh)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package v1alpha2
|
||||
|
||||
import (
|
||||
"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/models/iam/am"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam/im"
|
||||
@@ -16,7 +16,7 @@ type iamHandler struct {
|
||||
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{
|
||||
amOperator: am.NewAMOperator(k8sClient.Kubernetes(), factory.KubernetesSharedInformerFactory()),
|
||||
imOperator: im.NewLDAPOperator(ldapClient),
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"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/constants"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
@@ -38,7 +38,7 @@ const groupName = "iam.kubesphere.io"
|
||||
|
||||
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)
|
||||
|
||||
handler := newIAMHandler(k8sClient, factory, ldapClient, cacheClient, options)
|
||||
|
||||
@@ -25,19 +25,21 @@ import (
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/api/auth"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
|
||||
"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/request"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type oauthHandler struct {
|
||||
issuer token.Issuer
|
||||
config oauth.Configuration
|
||||
issuer token.Issuer
|
||||
options *authoptions.AuthenticationOptions
|
||||
}
|
||||
|
||||
func newOAUTHHandler(issuer token.Issuer, config oauth.Configuration) *oauthHandler {
|
||||
return &oauthHandler{issuer: issuer, config: config}
|
||||
func newOAUTHHandler(issuer token.Issuer, options *authoptions.AuthenticationOptions) *oauthHandler {
|
||||
return &oauthHandler{issuer: issuer, options: options}
|
||||
}
|
||||
|
||||
// Implement webhook authentication interface
|
||||
@@ -59,7 +61,7 @@ func (h *oauthHandler) TokenReviewHandler(req *restful.Request, resp *restful.Re
|
||||
return
|
||||
}
|
||||
|
||||
user, _, err := h.issuer.Verify(tokenReview.Spec.Token)
|
||||
user, err := h.issuer.Verify(tokenReview.Spec.Token)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
@@ -82,8 +84,9 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
|
||||
user, ok := request.UserFrom(req.Request.Context())
|
||||
clientId := req.QueryParameter("client_id")
|
||||
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 {
|
||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||
@@ -103,7 +106,13 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
|
||||
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 {
|
||||
err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err))
|
||||
@@ -111,11 +120,71 @@ func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Resp
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL := fmt.Sprintf("%s?access_token=%s&token_type=Bearer", conf.RedirectURL, accessToken)
|
||||
expiresIn := clm.ExpiresAt - clm.IssuedAt
|
||||
if expiresIn > 0 {
|
||||
redirectURL = fmt.Sprintf("%s&expires_in=%v", redirectURL, expiresIn)
|
||||
redirectURL, err := conf.ResolveRedirectURL(redirectURI)
|
||||
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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/auth"
|
||||
"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/constants"
|
||||
"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.Path("/oauth").
|
||||
Consumes(restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON)
|
||||
|
||||
handler := newOAUTHHandler(issuer, configuration)
|
||||
handler := newOAUTHHandler(issuer, options)
|
||||
|
||||
// Implement webhook authentication interface
|
||||
// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
|
||||
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{}).
|
||||
To(handler.TokenReviewHandler).
|
||||
Returns(http.StatusOK, api.StatusOK, auth.TokenReview{}).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
|
||||
|
||||
// TODO Built-in oauth2 server (provider)
|
||||
// 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.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
|
||||
// Only support implicit grant flow
|
||||
// https://tools.ietf.org/html/rfc6749#section-4.2
|
||||
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))
|
||||
//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)
|
||||
|
||||
|
||||
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{
|
||||
Name: "admin",
|
||||
Rego: "package authz\ndefault allow = false",
|
||||
Rego: `package authz
|
||||
default allow = false
|
||||
`,
|
||||
})
|
||||
return operator
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
var keys []string
|
||||
for k, _ := range s.store {
|
||||
for k := range s.store {
|
||||
if re.MatchString(k) {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ func (b *Build) GetRevisionBranch() string {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -98,6 +98,12 @@ func NewKubernetesClient(options *KubernetesOptions) (Client, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.istio, err = istioclient.NewForConfig(config)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.master = options.Master
|
||||
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 (
|
||||
"github.com/spf13/pflag"
|
||||
reflectutils "kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user