refine tenant api

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2019-04-01 02:59:19 +08:00
parent 744bd053e3
commit 93ad572e19
202 changed files with 13517 additions and 7951 deletions

187
Gopkg.lock generated
View File

@@ -142,6 +142,15 @@
packages = ["."]
revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20"
[[projects]]
branch = "master"
name = "github.com/docker/spdystream"
packages = [
".",
"spdy",
]
revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85"
[[projects]]
name = "github.com/dustin/go-humanize"
packages = ["."]
@@ -740,7 +749,7 @@
"api/prometheus/v1",
"prometheus",
"prometheus/internal",
"prometheus/promhttp"
"prometheus/promhttp",
]
revision = "505eaef017263e299324067d40ca2c48f6a2cf50"
version = "v0.9.2"
@@ -756,7 +765,7 @@
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model"
"model",
]
revision = "cfeb6f9992ffa54aaa4f2170ade4067ee478b250"
version = "v0.2.0"
@@ -769,7 +778,7 @@
"internal/util",
"iostats",
"nfs",
"xfs"
"xfs",
]
revision = "e56f2e22fc761e82a34aca553f6725e2aff4fe6c"
@@ -778,7 +787,7 @@
packages = [
"modfile",
"module",
"semver"
"semver",
]
revision = "1cf9852c553c5b7da2d5a4a091129a7822fed0c9"
version = "v1.2.2"
@@ -793,7 +802,7 @@
name = "github.com/spf13/afero"
packages = [
".",
"mem"
"mem",
]
revision = "f4711e4db9e9a1d3887343acb72b2bbfc2f686f5"
version = "v1.2.1"
@@ -820,7 +829,7 @@
name = "github.com/stretchr/testify"
packages = [
"assert",
"mock"
"mock",
]
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
version = "v1.3.0"
@@ -843,7 +852,7 @@
"lego",
"log",
"platform/wait",
"registration"
"registration",
]
revision = "2952cdaebd4da7cd560e195343bdd3cb78a67643"
version = "v2.3.0"
@@ -868,7 +877,7 @@
"internal/bufferpool",
"internal/color",
"internal/exit",
"zapcore"
"zapcore",
]
revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982"
version = "v1.9.1"
@@ -883,7 +892,7 @@
"hkdf",
"ocsp",
"pbkdf2",
"ssh/terminal"
"ssh/terminal",
]
revision = "a1f597ede03a7bef967a422b5b3a5bd08805a01e"
@@ -906,7 +915,7 @@
"internal/socks",
"ipv4",
"ipv6",
"proxy"
"proxy",
]
revision = "9f648a60d9775ef5c977e7669d1673a7a67bef33"
@@ -918,7 +927,7 @@
"google",
"internal",
"jws",
"jwt"
"jwt",
]
revision = "e64efc72b421e893cbf63f17ba2221e7d6d0b0f3"
@@ -928,8 +937,9 @@
packages = [
"cpu",
"unix",
"windows"
"windows",
]
revision = "fead79001313d15903fb4605b4a1b781532cd93e"
[[projects]]
@@ -961,7 +971,7 @@
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
"width"
"width",
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
@@ -976,7 +986,6 @@
branch = "master"
name = "golang.org/x/tools"
packages = [
"container/intsets",
"go/ast/astutil",
"go/gcexportdata",
"go/internal/cgo",
@@ -988,7 +997,7 @@
"internal/fastwalk",
"internal/gopathwalk",
"internal/module",
"internal/semver"
"internal/semver",
]
revision = "8b67d361bba210f5fbb3c1a0fc121e0847b10b57"
@@ -1005,7 +1014,7 @@
"internal/modules",
"internal/remote_api",
"internal/urlfetch",
"urlfetch"
"urlfetch",
]
revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
version = "v1.4.0"
@@ -1023,6 +1032,12 @@
source = "https://github.com/fsnotify/fsnotify.git"
version = "v1.4.7"
[[projects]]
name = "gopkg.in/igm/sockjs-go.v2"
packages = ["sockjs"]
revision = "d276e9ffe5cc5c271b81198cc77a2adf6c4482d2"
version = "v2.0.0"
[[projects]]
name = "gopkg.in/inf.v0"
packages = ["."]
@@ -1041,7 +1056,7 @@
".",
"cipher",
"json",
"jwt"
"jwt",
]
revision = "628223f44a71f715d2881ea69afc795a1e9c01be"
version = "v2.3.0"
@@ -1093,7 +1108,7 @@
"settings/v1alpha1",
"storage/v1",
"storage/v1alpha1",
"storage/v1beta1"
"storage/v1beta1",
]
revision = "05914d821849570fba9eacfb29466f2d8d3cd229"
version = "kubernetes-1.13.1"
@@ -1106,7 +1121,7 @@
"pkg/client/clientset/clientset",
"pkg/client/clientset/clientset/scheme",
"pkg/client/clientset/clientset/typed/apiextensions/v1beta1",
"pkg/features"
"pkg/features",
]
revision = "0fe22c71c47604641d9aa352c785b7912c200562"
version = "kubernetes-1.13.1"
@@ -1144,12 +1159,15 @@
"pkg/util/diff",
"pkg/util/errors",
"pkg/util/framer",
"pkg/util/httpstream",
"pkg/util/httpstream/spdy",
"pkg/util/intstr",
"pkg/util/json",
"pkg/util/mergepatch",
"pkg/util/naming",
"pkg/util/net",
"pkg/util/rand",
"pkg/util/remotecommand",
"pkg/util/runtime",
"pkg/util/sets",
"pkg/util/strategicpatch",
@@ -1161,7 +1179,8 @@
"pkg/version",
"pkg/watch",
"third_party/forked/golang/json",
"third_party/forked/golang/reflect"
"third_party/forked/golang/netutil",
"third_party/forked/golang/reflect",
]
revision = "2b1284ed4c93a43499e781493253e2ac5959c4fd"
version = "kubernetes-1.13.1"
@@ -1176,7 +1195,7 @@
"pkg/authorization/authorizer",
"pkg/endpoints/request",
"pkg/features",
"pkg/util/feature"
"pkg/util/feature",
]
revision = "3ccfe8365421eb08e334b195786a2973460741d8"
version = "kubernetes-1.13.1"
@@ -1318,17 +1337,20 @@
"tools/pager",
"tools/record",
"tools/reference",
"tools/remotecommand",
"tools/watch",
"transport",
"transport/spdy",
"util/buffer",
"util/cert",
"util/connrotation",
"util/exec",
"util/flowcontrol",
"util/homedir",
"util/integer",
"util/jsonpath",
"util/retry",
"util/workqueue"
"util/workqueue",
]
revision = "8d9ed539ba3134352c586810e749e58df4e94e4f"
version = "kubernetes-1.13.1"
@@ -1344,8 +1366,9 @@
"cmd/client-gen/generators/util",
"cmd/client-gen/path",
"cmd/client-gen/types",
"pkg/util"
"pkg/util",
]
pruneopts = "T"
revision = "c2090bec4d9b1fb25de3812f868accc2bc9ecbae"
version = "kubernetes-1.13.1"
@@ -1360,7 +1383,7 @@
"generator",
"namer",
"parser",
"types"
"types",
]
revision = "b90029ef6cd877cb3f422d75b3a07707e3aac6b7"
@@ -1407,7 +1430,7 @@
"pkg/util/net/sets",
"pkg/util/parsers",
"pkg/util/slice",
"pkg/util/taints"
"pkg/util/taints",
]
revision = "c27b913fddd1a6c480c229191a087698aa92f0b1"
version = "v1.13.4"
@@ -1433,6 +1456,7 @@
"pkg/client/apiutil",
"pkg/client/config",
"pkg/controller",
"pkg/controller/controllerutil",
"pkg/envtest",
"pkg/envtest/printer",
"pkg/event",
@@ -1461,7 +1485,7 @@
"pkg/webhook/internal/cert/writer",
"pkg/webhook/internal/cert/writer/atomic",
"pkg/webhook/internal/metrics",
"pkg/webhook/types"
"pkg/webhook/types",
]
revision = "12d98582e72927b6cd0123e2b4e819f9341ce62c"
version = "v0.1.10"
@@ -1478,7 +1502,7 @@
"pkg/rbac",
"pkg/util",
"pkg/webhook",
"pkg/webhook/internal"
"pkg/webhook/internal",
]
revision = "fbf141159251d035089e7acdd5a343f8cec91b94"
version = "v0.1.9"
@@ -1488,7 +1512,7 @@
packages = [
"integration",
"integration/addr",
"integration/internal"
"integration/internal",
]
revision = "d348cb12705b516376e0c323bacca72b00a78425"
version = "v0.1.1"
@@ -1502,6 +1526,111 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "662b6da91343ff0a611e4487b8eef803b103b676f5b2a5db7ed8351846218fc5"
input-imports = [
"github.com/dgrijalva/jwt-go",
"github.com/docker/docker/api/types",
"github.com/docker/docker/client",
"github.com/emicklei/go-restful",
"github.com/emicklei/go-restful-openapi",
"github.com/go-ldap/ldap",
"github.com/go-openapi/spec",
"github.com/go-redis/redis",
"github.com/go-sql-driver/mysql",
"github.com/golang/glog",
"github.com/google/uuid",
"github.com/jinzhu/gorm",
"github.com/json-iterator/go",
"github.com/kiali/kiali/config",
"github.com/kiali/kiali/handlers",
"github.com/knative/pkg/apis/istio/v1alpha3",
"github.com/knative/pkg/client/clientset/versioned",
"github.com/knative/pkg/client/informers/externalversions",
"github.com/knative/pkg/client/informers/externalversions/istio/v1alpha3",
"github.com/knative/pkg/client/listers/istio/v1alpha3",
"github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1",
"github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1",
"github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned",
"github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions",
"github.com/mholt/caddy",
"github.com/mholt/caddy/caddy/caddymain",
"github.com/mholt/caddy/caddyhttp/httpserver",
"github.com/onsi/ginkgo",
"github.com/onsi/gomega",
"github.com/spf13/cobra",
"github.com/spf13/pflag",
"golang.org/x/net/context",
"gopkg.in/igm/sockjs-go.v2/sockjs",
"gopkg.in/yaml.v2",
"k8s.io/api/apps/v1",
"k8s.io/api/apps/v1beta2",
"k8s.io/api/batch/v1",
"k8s.io/api/batch/v1beta1",
"k8s.io/api/core/v1",
"k8s.io/api/extensions/v1beta1",
"k8s.io/api/policy/v1beta1",
"k8s.io/api/rbac/v1",
"k8s.io/api/storage/v1",
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions",
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset",
"k8s.io/apimachinery/pkg/api/errors",
"k8s.io/apimachinery/pkg/api/resource",
"k8s.io/apimachinery/pkg/apis/meta/v1",
"k8s.io/apimachinery/pkg/fields",
"k8s.io/apimachinery/pkg/labels",
"k8s.io/apimachinery/pkg/runtime",
"k8s.io/apimachinery/pkg/runtime/schema",
"k8s.io/apimachinery/pkg/runtime/serializer",
"k8s.io/apimachinery/pkg/types",
"k8s.io/apimachinery/pkg/util/json",
"k8s.io/apimachinery/pkg/util/runtime",
"k8s.io/apimachinery/pkg/util/sets",
"k8s.io/apimachinery/pkg/util/wait",
"k8s.io/apimachinery/pkg/watch",
"k8s.io/apiserver/pkg/authentication/user",
"k8s.io/apiserver/pkg/authorization/authorizer",
"k8s.io/apiserver/pkg/endpoints/request",
"k8s.io/client-go/discovery",
"k8s.io/client-go/discovery/fake",
"k8s.io/client-go/informers",
"k8s.io/client-go/informers/apps/v1",
"k8s.io/client-go/informers/core/v1",
"k8s.io/client-go/kubernetes",
"k8s.io/client-go/kubernetes/scheme",
"k8s.io/client-go/kubernetes/typed/core/v1",
"k8s.io/client-go/listers/apps/v1",
"k8s.io/client-go/listers/core/v1",
"k8s.io/client-go/plugin/pkg/client/auth/gcp",
"k8s.io/client-go/rest",
"k8s.io/client-go/testing",
"k8s.io/client-go/tools/cache",
"k8s.io/client-go/tools/clientcmd",
"k8s.io/client-go/tools/record",
"k8s.io/client-go/tools/remotecommand",
"k8s.io/client-go/util/flowcontrol",
"k8s.io/client-go/util/workqueue",
"k8s.io/code-generator/cmd/client-gen",
"k8s.io/gengo/examples/deepcopy-gen/generators",
"k8s.io/gengo/examples/defaulter-gen/generators",
"k8s.io/klog",
"k8s.io/kubernetes/pkg/apis/core",
"k8s.io/kubernetes/pkg/controller",
"k8s.io/kubernetes/pkg/util/metrics",
"k8s.io/kubernetes/pkg/util/slice",
"sigs.k8s.io/application/pkg/controller/application",
"sigs.k8s.io/controller-runtime/pkg/client",
"sigs.k8s.io/controller-runtime/pkg/client/config",
"sigs.k8s.io/controller-runtime/pkg/controller",
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil",
"sigs.k8s.io/controller-runtime/pkg/envtest",
"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/runtime/log",
"sigs.k8s.io/controller-runtime/pkg/runtime/scheme",
"sigs.k8s.io/controller-runtime/pkg/runtime/signals",
"sigs.k8s.io/controller-runtime/pkg/source",
"sigs.k8s.io/controller-tools/cmd/controller-gen",
"sigs.k8s.io/testing_frameworks/integration",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -116,3 +116,7 @@ required = [
[[constraint]]
branch = "master"
name = "github.com/knative/pkg"
[[constraint]]
name = "gopkg.in/igm/sockjs-go.v2"
version = "2.0.0"

View File

@@ -64,7 +64,7 @@ deploy: manifests
# Generate DeepCopy to implement runtime.Object
deepcopy:
./vendor/k8s.io/code-generator/generate-groups.sh deepcopy,lister,informer,client kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "servicemesh:v1alpha2"
./vendor/k8s.io/code-generator/generate-groups.sh all kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "servicemesh:v1alpha2 tenant:v1alpha1"
# Generate code
generate:
@@ -79,7 +79,7 @@ docker-build: all
# Run tests
test: generate fmt vet
export KUBEBUILDER_CONTROLPLANE_START_TIMEOUT=1m; go test ./pkg/... ./cmd/... -coverprofile cover.out
go test ./pkg/... ./cmd/... -coverprofile cover.out
.PHONY: clean
clean:

View File

@@ -1,3 +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.
*/
package app
import (
@@ -6,7 +23,6 @@ import (
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/controller/destinationrule"
"kubesphere.io/kubesphere/pkg/controller/virtualservice"
"kubesphere.io/kubesphere/pkg/simple/controller/namespace"
"sigs.k8s.io/controller-runtime/pkg/manager"
"time"
@@ -60,11 +76,6 @@ func AddControllers(mgr manager.Manager, cfg *rest.Config, stopCh <-chan struct{
kubeClient,
istioclient)
nsController := namespace.NewNamespaceController(kubeClient,
informerFactory.Core().V1().Namespaces(),
informerFactory.Rbac().V1().Roles(),
)
servicemeshinformer.Start(stopCh)
istioInformer.Start(stopCh)
informerFactory.Start(stopCh)
@@ -72,7 +83,6 @@ func AddControllers(mgr manager.Manager, cfg *rest.Config, stopCh <-chan struct{
controllers := map[string]manager.Runnable{
"virtualservice-controller": vsController,
"destinationrule-controller": drController,
"namespace-controller": nsController,
}
for name, ctrl := range controllers {

View File

@@ -27,6 +27,8 @@ import (
_ "kubesphere.io/kubesphere/pkg/apis/operations/install"
_ "kubesphere.io/kubesphere/pkg/apis/resources/install"
_ "kubesphere.io/kubesphere/pkg/apis/servicemesh/metrics/install"
_ "kubesphere.io/kubesphere/pkg/apis/tenant/install"
_ "kubesphere.io/kubesphere/pkg/apis/terminal/install"
)
func main() {

View File

@@ -29,6 +29,4 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
s.GenericServerRunOptions.AddFlags(fs)
fs.StringVar(&s.IstioPilotServiceURL, "istio-pilot-service-url", "http://istio-pilot.istio-system.svc:8080/version", "istio pilot discovery service url")
fs.StringVar(&s.OpenPitrixServer, "openpitrix-server", "http://openpitrix-api-gateway.openpitrix-system.svc", "openpitrix server")
fs.StringVar(&s.OpenPitrixProxyToken, "openpitrix-proxy-token", "", "openpitrix proxy token")
}

View File

@@ -29,8 +29,10 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/filter"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
logging "kubesphere.io/kubesphere/pkg/models/log"
"kubesphere.io/kubesphere/pkg/signals"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"log"
"net/http"
)
@@ -70,28 +72,43 @@ func Run(s *options.ServerRunOptions) error {
container := runtime.Container
container.Filter(filter.Logging)
log.Printf("Server listening on %d.", s.GenericServerRunOptions.InsecurePort)
for _, webservice := range container.RegisteredWebServices() {
for _, route := range webservice.Routes() {
log.Printf(route.Path)
log.Println(route.Method, route.Path)
}
}
initializeESClientConfig()
initializeKialiConfig(s)
err = initializeDatabase()
if err != nil {
return err
}
if s.GenericServerRunOptions.InsecurePort != 0 {
log.Printf("Server listening on %d.", s.GenericServerRunOptions.InsecurePort)
err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.InsecurePort), container)
}
if s.GenericServerRunOptions.SecurePort != 0 && len(s.GenericServerRunOptions.TlsCertFile) > 0 && len(s.GenericServerRunOptions.TlsPrivateKey) > 0 {
log.Printf("Server listening on %d.", s.GenericServerRunOptions.SecurePort)
err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.SecurePort), s.GenericServerRunOptions.TlsCertFile, s.GenericServerRunOptions.TlsPrivateKey, container)
}
return err
}
func initializeDatabase() error {
db := mysql.Client()
if !db.HasTable(&models.WorkspaceDPBinding{}) {
if err := db.CreateTable(&models.WorkspaceDPBinding{}).Error; err != nil {
return err
}
}
return nil
}
func initializeKialiConfig(s *options.ServerRunOptions) {
// Initialize kiali config
config := kconfig.NewConfig()
@@ -114,7 +131,7 @@ func initializeKialiConfig(s *options.ServerRunOptions) {
func initializeESClientConfig() {
// List all outputs
outputs,err := logging.GetFluentbitOutputFromConfigMap()
outputs, err := logging.GetFluentbitOutputFromConfigMap()
if err != nil {
glog.Errorln(err)
return
@@ -153,9 +170,11 @@ func waitForResourceSync() {
informerFactory.Apps().V1().StatefulSets().Lister()
informerFactory.Apps().V1().Deployments().Lister()
informerFactory.Apps().V1().DaemonSets().Lister()
informerFactory.Apps().V1().ReplicaSets().Lister()
informerFactory.Batch().V1().Jobs().Lister()
informerFactory.Batch().V1beta1().CronJobs().Lister()
informerFactory.Extensions().V1beta1().Ingresses().Lister()
informerFactory.Start(stopChan)
informerFactory.WaitForCacheSync(stopChan)
@@ -167,5 +186,12 @@ func waitForResourceSync() {
s2iInformerFactory.Start(stopChan)
s2iInformerFactory.WaitForCacheSync(stopChan)
ksInformerFactory := informers.KsSharedInformerFactory()
ksInformerFactory.Tenant().V1alpha1().Workspaces().Lister()
ksInformerFactory.Start(stopChan)
ksInformerFactory.WaitForCacheSync(stopChan)
log.Println("resources sync success")
}

View File

@@ -1,3 +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.
*/
package options
import (
@@ -10,6 +27,7 @@ type ServerRunOptions struct {
AdminEmail string
AdminPassword string
TokenExpireTime string
JWTSecret string
}
func NewServerRunOptions() *ServerRunOptions {
@@ -22,6 +40,7 @@ func NewServerRunOptions() *ServerRunOptions {
func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.AdminEmail, "admin-email", "admin@kubesphere.io", "default administrator's email")
fs.StringVar(&s.AdminPassword, "admin-password", "passw0rd", "default administrator's password")
fs.StringVar(&s.TokenExpireTime, "token-expire-time", "24h", "token expire time")
fs.StringVar(&s.TokenExpireTime, "token-expire-time", "2h", "token expire time,valid time units are \"ns\",\"us\",\"ms\",\"s\",\"m\",\"h\"")
fs.StringVar(&s.JWTSecret, "jwt-secret", "", "jwt secret")
s.GenericServerRunOptions.AddFlags(fs)
}

View File

@@ -29,6 +29,7 @@ import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/signals"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
"log"
"net/http"
"time"
@@ -54,14 +55,12 @@ cluster's shared state through which all other components interact.`,
}
func Run(s *options.ServerRunOptions) error {
pflag.VisitAll(func(flag *pflag.Flag) {
log.Printf("FLAG: --%s=%q", flag.Name, flag.Value)
})
var err error
expireTime, err := time.ParseDuration(s.TokenExpireTime)
if err != nil {
@@ -69,6 +68,7 @@ func Run(s *options.ServerRunOptions) error {
}
err = iam.Init(s.AdminEmail, s.AdminPassword, expireTime)
jwtutil.Setup(s.JWTSecret)
if err != nil {
return err
@@ -79,11 +79,19 @@ func Run(s *options.ServerRunOptions) error {
container := runtime.Container
container.Filter(filter.Logging)
for _, webservice := range container.RegisteredWebServices() {
for _, route := range webservice.Routes() {
log.Println(route.Method, route.Path)
}
}
if s.GenericServerRunOptions.InsecurePort != 0 {
log.Printf("Server listening on %d.", s.GenericServerRunOptions.InsecurePort)
err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.InsecurePort), container)
}
if s.GenericServerRunOptions.SecurePort != 0 && len(s.GenericServerRunOptions.TlsCertFile) > 0 && len(s.GenericServerRunOptions.TlsPrivateKey) > 0 {
log.Printf("Server listening on %d.", s.GenericServerRunOptions.SecurePort)
err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.SecurePort), s.GenericServerRunOptions.TlsCertFile, s.GenericServerRunOptions.TlsPrivateKey, container)
}
@@ -103,5 +111,11 @@ func waitForResourceSync() {
informerFactory.Start(stopChan)
informerFactory.WaitForCacheSync(stopChan)
ksInformerFactory := informers.KsSharedInformerFactory()
ksInformerFactory.Tenant().V1alpha1().Workspaces().Lister()
ksInformerFactory.Start(stopChan)
ksInformerFactory.WaitForCacheSync(stopChan)
log.Println("resources sync success")
}

View File

@@ -0,0 +1,50 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
labels:
controller-tools.k8s.io: "1.0"
name: workspaces.tenant.kubesphere.io
spec:
group: tenant.kubesphere.io
names:
kind: Workspace
plural: workspaces
scope: Cluster
validation:
openAPIV3Schema:
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/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/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
properties:
manager:
type: string
quotas:
type: object
type: object
status:
properties:
quotas:
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
of cluster Important: Run "make" to regenerate code after modifying
this file'
type: object
type: object
version: v1alpha1
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -4,84 +4,100 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- networking.istio.io
resources:
- virtualservices
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- networking.istio.io
resources:
- virtualservices/status
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- servicemesh.kubesphere.io
resources:
- strategies
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- servicemesh.kubesphere.io
resources:
- strategies/status
verbs:
- get
- update
- patch
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
- validatingwebhookconfigurations
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- namespaces/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- namespaces/status
verbs:
- get
- update
- patch
- apiGroups:
- tenant.kubesphere.io
resources:
- workspaces
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- tenant.kubesphere.io
resources:
- workspaces/status
verbs:
- get
- update
- patch
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
- validatingwebhookconfigurations
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- create
- update
- patch
- delete

View File

@@ -0,0 +1,8 @@
apiVersion: tenant.kubesphere.io/v1alpha1
kind: Workspace
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: workspace-sample
spec:
manager: admin

View File

@@ -2,6 +2,6 @@
docker build -f build/ks-apigateway/Dockerfile -t kubespheredev/ks-apigateway:latest .
docker build -f build/ks-apiserver/Dockerfile -t kubespheredev/ks-apiserver:latest .
docker build -f build/ks-iam/Dockerfile -t kubespheredev/ks-iam:latest .
docker build -f build/ks-iam/Dockerfile -t kubespheredev/ks-account:latest .
docker build -f build/controller-manager/Dockerfile -t kubespheredev/ks-controller-manager:latest .

View File

@@ -5,5 +5,5 @@
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker push kubespheredev/ks-apigateway:latest
docker push kubespheredev/ks-apiserver:latest
docker push kubespheredev/ks-iam:latest
docker push kubespheredev/ks-account:latest
docker push kubespheredev/ks-controller-manager:latest

View File

@@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/endpoints/request"
"log"
"net/http"
"strconv"
"strings"
@@ -50,7 +51,7 @@ type User struct {
}
var requestInfoFactory = request.RequestInfoFactory{
APIPrefixes: sets.NewString("api", "apis"),
APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"),
GrouplessAPIPrefixes: sets.NewString("api")}
func (h Auth) ServeHTTP(resp http.ResponseWriter, req *http.Request) (int, error) {
@@ -71,6 +72,7 @@ func (h Auth) ServeHTTP(resp http.ResponseWriter, req *http.Request) (int, error
token, err := h.Validate(uToken)
if err != nil {
log.Println(uToken)
return h.HandleUnauthorized(resp, err), nil
}
@@ -166,6 +168,7 @@ func (h Auth) Validate(uToken string) (*jwt.Token, error) {
func (h Auth) HandleUnauthorized(w http.ResponseWriter, err error) int {
message := fmt.Sprintf("Unauthorized,%v", err)
w.Header().Add("WWW-Authenticate", message)
log.Println(message)
return http.StatusUnauthorized
}

View File

@@ -33,7 +33,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/informers"
sliceutils "kubesphere.io/kubesphere/pkg/utils"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
)
type Authentication struct {
@@ -87,6 +87,10 @@ func handleForbidden(w http.ResponseWriter, err error) int {
func permissionValidate(attrs authorizer.Attributes) (bool, error) {
if attrs.GetResource() == "users" && attrs.GetUser().GetName() == attrs.GetName() {
return true, nil
}
permitted, err := clusterRoleValidate(attrs)
if err != nil {
@@ -164,7 +168,7 @@ func clusterRoleValidate(attrs authorizer.Attributes) (bool, error) {
for _, subject := range clusterRoleBinding.Subjects {
if (subject.Kind == v1.UserKind && subject.Name == attrs.GetUser().GetName()) ||
(subject.Kind == v1.GroupKind && sliceutils.HasString(attrs.GetUser().GetGroups(), subject.Name)) {
(subject.Kind == v1.GroupKind && sliceutil.HasString(attrs.GetUser().GetGroups(), subject.Name)) {
clusterRole, err := clusterRoleLister.Get(clusterRoleBinding.RoleRef.Name)
@@ -198,11 +202,11 @@ func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string,
return false
}
if !sliceutils.HasString(rule.APIGroups, apiGroup) && !sliceutils.HasString(rule.APIGroups, v1.ResourceAll) {
if !sliceutil.HasString(rule.APIGroups, apiGroup) && !sliceutil.HasString(rule.APIGroups, v1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !sliceutils.HasString(rule.ResourceNames, resourceName) {
if len(rule.ResourceNames) > 0 && !sliceutil.HasString(rule.ResourceNames, resourceName) {
return false
}
@@ -234,7 +238,7 @@ func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string,
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !sliceutils.HasString(rule.Verbs, verb) && !sliceutils.HasString(rule.Verbs, v1.VerbAll) {
if !sliceutil.HasString(rule.Verbs, verb) && !sliceutil.HasString(rule.Verbs, v1.VerbAll) {
return false
}

View File

@@ -25,7 +25,6 @@ import (
"github.com/mholt/caddy/caddyhttp/httpserver"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/signals"
)
func init() {
@@ -43,13 +42,8 @@ func Setup(c *caddy.Controller) error {
if err != nil {
return err
}
if err != nil {
return err
}
stopChan := make(chan struct{}, 0)
c.OnStartup(func() error {
stopChan := signals.SetupSignalHandler()
informerFactory := informers.SharedInformerFactory()
informerFactory.Rbac().V1().Roles().Lister()
informerFactory.Rbac().V1().RoleBindings().Lister()
@@ -61,6 +55,11 @@ func Setup(c *caddy.Controller) error {
return nil
})
c.OnShutdown(func() error {
close(stopChan)
return nil
})
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
return &Authentication{Next: next, Rule: rule}
})

View File

@@ -0,0 +1,10 @@
package apis
import (
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
)
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
}

View File

@@ -20,7 +20,6 @@ package v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/iam"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
@@ -46,6 +45,7 @@ func addWebService(c *restful.Container) error {
Doc("Token review").
Reads(iam.TokenReview{}).
Writes(iam.TokenReview{}).
Doc("k8s token review").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/login").
To(iam.LoginHandler).
@@ -53,10 +53,10 @@ func addWebService(c *restful.Container) error {
Reads(iam.LoginRequest{}).
Writes(models.Token{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users/{name}").
To(iam.UserDetail).
ws.Route(ws.GET("/users/{username}").
To(iam.DescribeUser).
Doc("User detail").
Param(ws.PathParameter("username", "username")).
Writes(models.User{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/users").
@@ -67,11 +67,13 @@ func addWebService(c *restful.Container) error {
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/users/{name}").
To(iam.DeleteUser).
Param(ws.PathParameter("name", "username")).
Doc("Delete user").
Writes(errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.PUT("/users/{name}").
To(iam.UpdateUser).
Param(ws.PathParameter("name", "username")).
Reads(models.User{}).
Writes(errors.Error{}).
Doc("Update user").
@@ -79,26 +81,29 @@ func addWebService(c *restful.Container) error {
ws.Route(ws.GET("/users/{name}/log").
To(iam.UserLoginLog).
Param(ws.PathParameter("name", "username")).
Doc("User login log").
Writes([]map[string]string{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users").
To(iam.UserList).
To(iam.ListUsers).
Doc("User list").
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/groups").
To(iam.RootGroupList).
To(iam.ListGroups).
Writes([]models.Group{}).
Doc("User group list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/groups/{path}").
To(iam.GroupDetail).
To(iam.DescribeGroup).
Param(ws.PathParameter("path", "group path")).
Doc("User group detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/groups/{path}/users").
To(iam.GroupUsers).
To(iam.ListGroupUsers).
Param(ws.PathParameter("path", "group path")).
Doc("Group user list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/groups").
@@ -108,139 +113,108 @@ func addWebService(c *restful.Container) error {
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/groups/{path}").
To(iam.DeleteGroup).
Param(ws.PathParameter("path", "group path")).
Doc("Delete user group").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.PUT("/groups/{path}").
To(iam.UpdateGroup).
Param(ws.PathParameter("path", "group path")).
Doc("Update user group").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users/{username}/roles").
To(iam.UserRoles).
To(iam.ListUserRoles).
Param(ws.PathParameter("username", "username")).
Doc("Get user role list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/roles").
To(iam.ListRoles).
Param(ws.PathParameter("namespace", "namespace")).
Doc("Get role list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/clusterroles").
To(iam.ListClusterRoles).
Doc("Get cluster role list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users").
To(iam.RoleUsers).
To(iam.ListRoleUsers).
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("role", "role name")).
Doc("Get user list by role").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules").
To(iam.RoleRules).
Doc("Get role detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/users").
To(iam.NamespaceUsers).
To(iam.ListNamespaceUsers).
Param(ws.PathParameter("namespace", "namespace")).
Doc("Get user list by namespace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/clusterroles/{clusterrole}/users").
To(iam.ClusterRoleUsers).
To(iam.ListClusterRoleUsers).
Param(ws.PathParameter("clusterrole", "cluster role name")).
Doc("Get user list by cluster role").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/clusterroles/{clusterrole}/rules").
To(iam.ClusterRoleRules).
To(iam.ListClusterRoleRules).
Param(ws.PathParameter("clusterrole", "cluster role name")).
Doc("Get cluster role detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/rulesmapping/clusterroles").
To(iam.ClusterRulesMappingHandler).
To(iam.ClusterRulesMapping).
Doc("Get cluster role policy rules mapping").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/rulesmapping/roles").
To(iam.RulesMappingHandler).
To(iam.RulesMapping).
Doc("Get role policy rules mapping").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/rules").
To(iam.WorkspaceRulesHandler).
Doc("Get workspace level policy rules").
ws.Route(ws.GET("/workspaces/{workspace}/roles").
To(iam.ListWorkspaceRoles).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List workspace role").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}").
To(iam.DescribeWorkspaceRole).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("role", "workspace role name")).
Doc("Describe workspace role").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}/rules").
To(iam.ListWorkspaceRoleRules).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("role", "workspace role name")).
Doc("Get workspace role policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/members").
To(iam.WorkspaceMemberList).
To(iam.ListWorkspaceUsers).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Get workspace member list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/rules").
To(iam.NamespacesRulesHandler).
Doc("Get namespace level policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/devops/{devops}/rules").
To(iam.DevopsRulesHandler).
Doc("Get devops project level policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
tags = []string{"Workspace"}
ws.Route(ws.GET("/workspaces").
To(iam.UserWorkspaceListHandler).
Doc("Get workspace list").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes([]models.Workspace{}))
ws.Route(ws.POST("/workspaces").
To(iam.WorkspaceCreateHandler).
Doc("Create workspace").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.Workspace{}))
ws.Route(ws.DELETE("/workspaces/{name}").
To(iam.DeleteWorkspaceHandler).
Doc("Delete workspace").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(errors.Error{}))
ws.Route(ws.GET("/workspaces/{name}").
To(iam.WorkspaceDetailHandler).
Doc("Get workspace detail").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.Workspace{}))
ws.Route(ws.PUT("/workspaces/{name}").
To(iam.WorkspaceEditHandler).
Doc("Update workspace").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.Workspace{}))
ws.Route(ws.GET("/workspaces/{name}/members/{member}").
To(iam.WorkspaceMemberDetail).
Doc("Get workspace member detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{name}/roles").
To(iam.WorkspaceRoles).
Doc("Get workspace roles").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/workspaces/{name}/members").
To(iam.WorkspaceMemberInvite).
ws.Route(ws.POST("/workspaces/{workspace}/members").
To(iam.InviteUser).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Add user to workspace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/workspaces/{name}/members").
To(iam.WorkspaceMemberRemove).
Doc("Delete user from workspace").
ws.Route(ws.POST("/workspaces/{workspace}/members").
To(iam.RemoveUser).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Remove user from workspace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}").
To(iam.DescribeWorkspaceUser).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("username", "username")).
Doc("Describe user in workspace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules").
To(iam.ListRoleRules).
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("role", "role name")).
Doc("Get namespace role policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/devops/{devops}/roles/{role}/rules").
To(iam.ListDevopsRoleRules).
Param(ws.PathParameter("devops", "devops project id")).
Param(ws.PathParameter("role", "devops role name")).
Doc("Get devops role policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
tags = []string{"unstable"}
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
To(iam.UserNamespaceListHandler).
Doc("Get namespace list").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.PageableResponse{}))
ws.Route(ws.POST("/workspaces/{name}/namespaces").
To(iam.NamespaceCreateHandler).
Doc("Create namespace").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(v1.Namespace{}))
ws.Route(ws.DELETE("/workspaces/{name}/namespaces/{namespace}").To(iam.NamespaceDeleteHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{name}/namespaces/{namespace}").To(iam.NamespaceCheckHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}").To(iam.NamespaceCheckHandler))
// TODO move to /apis/resources.kubesphere.io/workspaces/{workspace}/members/{username}
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}/namespaces").To(iam.NamespacesListHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{name}/members/{username}/devops").To(iam.DevOpsProjectHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
// TODO /workspaces/{name}/roles/{role}
ws.Route(ws.GET("/workspaces/{name}/roles/{role}").To(iam.WorkspaceRoles).Metadata(restfulspec.KeyOpenAPITags, tags))
// TODO move to /apis/resources.kubesphere.io/devops
ws.Route(ws.GET("/workspaces/{name}/devops").To(iam.DevOpsProjectHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/workspaces/{name}/devops").To(iam.DevOpsProjectCreateHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/workspaces/{name}/devops/{id}").To(iam.DevOpsProjectDeleteHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
// TODO merge into /groups
ws.Route(ws.GET("/groups/count").To(iam.CountHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users/{name}/namespaces").To(iam.NamespacesListHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
c.Add(ws)
return nil
}

View File

@@ -214,4 +214,4 @@ func addWebService(c *restful.Container) error {
c.Add(ws)
return nil
}
}

View File

@@ -52,7 +52,7 @@ func addWebService(c *restful.Container) error {
tags := []string{"Namespace resources"}
webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}").
To(resources.NamespaceResourceHandler).
To(resources.ListResources).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Namespace level resource query").
Param(webservice.PathParameter("namespace", "which namespace")).
@@ -69,7 +69,7 @@ func addWebService(c *restful.Container) error {
tags = []string{"Cluster resources"}
webservice.Route(webservice.GET("/{resources}").
To(resources.ClusterResourceHandler).
To(resources.ListResources).
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Cluster level resource query").
@@ -196,26 +196,19 @@ func addWebService(c *restful.Container) error {
webservice.Route(webservice.GET("/routers").
To(routers.GetAllRouters).
Doc("Get all routers").
Doc("List all routers").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(corev1.Service{}))
webservice.Route(webservice.GET("/users/{username}/routers").
To(routers.GetAllRoutersOfUser).
Doc("Get routers for user").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("username", "")).
Writes(corev1.Service{}))
webservice.Route(webservice.GET("/namespaces/{namespace}/router").
To(routers.GetRouter).
Doc("Get router of a specified project").
Doc("List router of a specified project").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "name of the project")))
webservice.Route(webservice.DELETE("/namespaces/{namespace}/router").
To(routers.DeleteRouter).
Doc("Get router of a specified project").
Doc("List router of a specified project").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "name of the project")))

20
pkg/apis/tenant/group.go Normal file
View 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.
*/
// Package tenant contains tenant API versions
package tenant

View File

@@ -0,0 +1,33 @@
/*
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 install
import (
"github.com/emicklei/go-restful"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
func init() {
Install(runtime.Container)
}
func Install(container *restful.Container) {
urlruntime.Must(tenantv1alpha2.AddToContainer(container))
}

View File

@@ -0,0 +1,25 @@
/*
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 v1alpha1 contains API Schema definitions for the tenant v1alpha1 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/tenant
// +k8s:defaulter-gen=TypeMeta
// +groupName=tenant.kubesphere.io
package v1alpha1

View File

@@ -0,0 +1,48 @@
/*
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 v1alpha1 contains API Schema definitions for the tenant v1alpha1 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/tenant
// +k8s:defaulter-gen=TypeMeta
// +groupName=tenant.kubesphere.io
package v1alpha1
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: "tenant.kubesphere.io", Version: "v1alpha1"}
// 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()
}

View File

@@ -0,0 +1,57 @@
/*
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 v1alpha1
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)
}

View File

@@ -0,0 +1,67 @@
/*
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 v1alpha1
import (
"k8s.io/api/core/v1"
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.
// WorkspaceSpec defines the desired state of Workspace
type WorkspaceSpec struct {
Manager string `json:"manager,omitempty"`
Quotas v1.ResourceQuotaSpec `json:"quotas,omitempty"`
}
// WorkspaceStatus defines the observed state of Workspace
type WorkspaceStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
Quotas v1.ResourceQuotaStatus `json:"quotas,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient:nonNamespaced
// Workspace is the Schema for the workspaces API
// +k8s:openapi-gen=true
type Workspace struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec WorkspaceSpec `json:"spec,omitempty"`
Status WorkspaceStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient:nonNamespaced
// WorkspaceList contains a list of Workspace
type WorkspaceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Workspace `json:"items"`
}
func init() {
SchemeBuilder.Register(&Workspace{}, &WorkspaceList{})
}

View File

@@ -0,0 +1,58 @@
/*
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 v1alpha1
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 TestStorageWorkspace(t *testing.T) {
key := types.NamespacedName{
Name: "foo",
}
created := &Workspace{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
}}
g := gomega.NewGomegaWithT(t)
// Test Create
fetched := &Workspace{}
g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
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)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
g.Expect(fetched).To(gomega.Equal(updated))
// Test Delete
g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred())
}

View File

@@ -0,0 +1,120 @@
// +build !ignore_autogenerated
/*
Copyright The Kubernetes 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 deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Workspace) DeepCopyInto(out *Workspace) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workspace.
func (in *Workspace) DeepCopy() *Workspace {
if in == nil {
return nil
}
out := new(Workspace)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Workspace) 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 *WorkspaceList) DeepCopyInto(out *WorkspaceList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Workspace, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceList.
func (in *WorkspaceList) DeepCopy() *WorkspaceList {
if in == nil {
return nil
}
out := new(WorkspaceList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WorkspaceList) 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 *WorkspaceSpec) DeepCopyInto(out *WorkspaceSpec) {
*out = *in
in.Quotas.DeepCopyInto(&out.Quotas)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceSpec.
func (in *WorkspaceSpec) DeepCopy() *WorkspaceSpec {
if in == nil {
return nil
}
out := new(WorkspaceSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceStatus) DeepCopyInto(out *WorkspaceStatus) {
*out = *in
in.Quotas.DeepCopyInto(&out.Quotas)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceStatus.
func (in *WorkspaceStatus) DeepCopy() *WorkspaceStatus {
if in == nil {
return nil
}
out := new(WorkspaceStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -0,0 +1,107 @@
/*
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 (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/apiserver/tenant"
)
const GroupName = "tenant.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
tags := []string{"Tenant"}
ws := runtime.NewWebService(GroupVersion)
ws.Route(ws.GET("/workspaces").
To(tenant.ListWorkspaces).
Doc("List workspace by user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/rules").
To(tenant.ListWorkspaceRules).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the rules for the current user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/rules").
To(tenant.ListNamespaceRules).
Param(ws.PathParameter("namespace", "namespace")).
Doc("List the rules for the current user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/devops/{devops}/rules").
To(tenant.ListDevopsRules).
Param(ws.PathParameter("devops", "devops project id")).
Doc("List the rules for the current user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
To(tenant.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces for the current user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}/namespaces").
To(tenant.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("username", "workspace member's username")).
Doc("List the namespaces for the workspace member").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/workspaces/{workspace}/namespaces").
To(tenant.CreateNamespace).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create namespace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}").
To(tenant.DeleteNamespace).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("namespace", "namespace")).
Doc("Delete namespace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/devops").
To(tenant.ListDevopsProjects).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List devops projects for the current user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}/devops").
To(tenant.ListDevopsProjects).
Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("username", "workspace member's username")).
Doc("List the devops projects for the workspace member").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/workspaces/{workspace}/devops").
To(tenant.CreateDevopsProject).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create devops project").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/workspaces/{workspace}/devops").
To(tenant.DeleteDevopsProject).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("Delete devops project").
Metadata(restfulspec.KeyOpenAPITags, tags))
c.Add(ws)
return nil
}

View File

@@ -0,0 +1,33 @@
/*
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 install
import (
"github.com/emicklei/go-restful"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
terminalv1alpha2 "kubesphere.io/kubesphere/pkg/apis/terminal/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
func init() {
Install(runtime.Container)
}
func Install(c *restful.Container) {
urlruntime.Must(terminalv1alpha2.AddToContainer(c))
}

View File

@@ -0,0 +1,56 @@
/*
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 (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/apiserver/terminal"
"kubesphere.io/kubesphere/pkg/models"
)
const GroupName = "terminal.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
webservice := runtime.NewWebService(GroupVersion)
tags := []string{"Terminal"}
webservice.Route(webservice.GET("/namespace/{namespace}/pods/{pods}").
To(terminal.CreateTerminalSession).
Doc("create terminal session").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.PodInfo{}))
path := runtime.ApiRootPath + "/" + GroupVersion.String() + "/sockjs"
c.Handle(path, terminal.NewTerminalHandler(path))
c.Add(webservice)
return nil
}

View File

@@ -20,42 +20,22 @@ package iam
import (
"github.com/emicklei/go-restful"
"k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/params"
"net/http"
"sort"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
)
type roleList struct {
ClusterRoles []*v1.ClusterRole `json:"clusterRoles" protobuf:"bytes,2,rep,name=clusterRoles"`
ClusterRoles []*v1.ClusterRole `json:"clusterRole" protobuf:"bytes,2,rep,name=clusterRoles"`
Roles []*v1.Role `json:"roles" protobuf:"bytes,2,rep,name=roles"`
}
func RoleRules(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
roleName := req.PathParameter("role")
role, err := iam.GetRole(namespace, roleName)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
rules, err := iam.GetRoleSimpleRules([]*v1.Role{role}, namespace)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules[namespace])
}
func RoleUsers(req *restful.Request, resp *restful.Response) {
func ListRoleUsers(req *restful.Request, resp *restful.Response) {
roleName := req.PathParameter("role")
namespace := req.PathParameter("namespace")
@@ -69,7 +49,53 @@ func RoleUsers(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(users)
}
func NamespaceUsers(req *restful.Request, resp *restful.Response) {
func ListClusterRoles(req *restful.Request, resp *restful.Response) {
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := iam.ListClusterRoles(conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func ListRoles(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := iam.ListRoles(namespace, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
// List users by namespace
func ListNamespaceUsers(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
@@ -80,25 +106,26 @@ func NamespaceUsers(req *restful.Request, resp *restful.Response) {
return
}
// sort by time by default
sort.Slice(users, func(i, j int) bool {
return users[i].Username < users[j].Username
return users[i].RoleBindTime.After(*users[j].RoleBindTime)
})
resp.WriteAsJson(users)
}
func UserRoles(req *restful.Request, resp *restful.Response) {
func ListUserRoles(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("username")
roles, err := iam.GetRoles(username, "")
roles, err := iam.GetUserRoles("", username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRoles, err := iam.GetClusterRoles(username)
_, clusterRoles, err := iam.GetUserClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
@@ -112,79 +139,62 @@ func UserRoles(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(roleList)
}
func NamespaceRulesHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
username := req.HeaderParameter(constants.UserNameHeader)
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
roles, err := iam.GetRoles(username, namespace)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
for _, clusterRole := range clusterRoles {
role := new(v1.Role)
role.Name = clusterRole.Name
role.Labels = clusterRole.Labels
role.Namespace = namespace
role.Annotations = clusterRole.Annotations
role.Kind = "Role"
role.Rules = clusterRole.Rules
roles = append(roles, role)
}
rules, err := iam.GetRoleSimpleRules(roles, namespace)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules[namespace])
}
func RulesMappingHandler(req *restful.Request, resp *restful.Response) {
func RulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.RoleRuleMapping
resp.WriteAsJson(rules)
}
func ClusterRulesMappingHandler(req *restful.Request, resp *restful.Response) {
func ClusterRulesMapping(req *restful.Request, resp *restful.Response) {
rules := policy.ClusterRoleRuleMapping
resp.WriteAsJson(rules)
}
func ClusterRoleRules(req *restful.Request, resp *restful.Response) {
func ListClusterRoleRules(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
clusterRole, err := iam.GetClusterRole(clusterRoleName)
rules, err := iam.GetClusterRoleSimpleRules(clusterRoleName)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
rules, err := iam.GetClusterRoleSimpleRules([]*v1.ClusterRole{clusterRole})
resp.WriteAsJson(rules)
}
func ListClusterRoleUsers(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := iam.ListClusterRoleUsers(clusterRoleName, conditions, orderBy, reverse, limit, offset)
if err != nil {
if k8serr.IsNotFound(err) {
resp.WriteError(http.StatusNotFound, err)
} else {
resp.WriteError(http.StatusInternalServerError, err)
}
return
}
resp.WriteAsJson(result)
}
func ListRoleRules(req *restful.Request, resp *restful.Response) {
namespaceName := req.PathParameter("namespace")
roleName := req.PathParameter("role")
rules, err := iam.GetRoleSimpleRules(namespaceName, roleName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(rules)
}
func ClusterRoleUsers(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
users, err := iam.ClusterRoleUsers(clusterRoleName)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(users)
}

View File

@@ -18,17 +18,14 @@
package iam
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/utils/iputil"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/utils"
jwtutils "kubesphere.io/kubesphere/pkg/utils/jwt"
)
type Spec struct {
@@ -63,11 +60,11 @@ func LoginHandler(req *restful.Request, resp *restful.Response) {
err := req.ReadEntity(&loginRequest)
if err != nil || loginRequest.Username == "" || loginRequest.Password == "" {
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(fmt.Errorf("incorrect username or password")))
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.New("incorrect username or password"))
return
}
ip := utils.RemoteIp(req.Request)
ip := iputil.RemoteIp(req.Request)
token, err := iam.Login(loginRequest.Username, loginRequest.Password, ip)
@@ -76,7 +73,7 @@ func LoginHandler(req *restful.Request, resp *restful.Response) {
return
}
resp.WriteAsJson(models.Token{Token: token})
resp.WriteAsJson(token)
}
// k8s token review
@@ -91,13 +88,13 @@ func TokenReviewHandler(req *restful.Request, resp *restful.Response) {
}
if tokenReview.Spec == nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("token must not be null")))
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("token must not be null"))
return
}
uToken := tokenReview.Spec.Token
token, err := jwtutils.ValidateToken(uToken)
token, err := jwtutil.ValidateToken(uToken)
if err != nil {
failed := TokenReview{APIVersion: APIVersion,
@@ -112,24 +109,29 @@ func TokenReviewHandler(req *restful.Request, resp *restful.Response) {
claims := token.Claims.(jwt.MapClaims)
username := claims["username"].(string)
username, ok := claims["username"].(string)
conn, err := ldap.Client()
if !ok {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("username not found"))
return
}
user, err := iam.GetUserInfo(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
user, err := iam.UserDetail(username, conn)
groups, err := iam.GetUserGroups(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.Groups = groups
success := TokenReview{APIVersion: APIVersion,
Kind: KindTokenReview,
Status: &Status{

View File

@@ -19,7 +19,6 @@ package iam
import (
"fmt"
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"net/http"
"regexp"
"strings"
@@ -33,8 +32,6 @@ import (
)
func CreateGroup(req *restful.Request, resp *restful.Response) {
//var json map[string]interface{}
var group models.Group
err := req.ReadEntity(&group)
@@ -45,19 +42,18 @@ func CreateGroup(req *restful.Request, resp *restful.Response) {
}
if !regexp.MustCompile("[a-z0-9]([-a-z0-9]*[a-z0-9])?").MatchString(group.Name) {
resp.WriteHeaderAndEntity(http.StatusBadRequest, fmt.Errorf("incalid group name %s", group))
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(fmt.Sprintf("incalid group name %s", group)))
return
}
if group.Creator == "" {
resp.WriteHeaderAndEntity(http.StatusBadRequest, fmt.Errorf("creator should not be null"))
return
}
created, err := iam.CreateGroup(group)
created, err := iam.CreateGroup(&group)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) {
resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
@@ -75,6 +71,10 @@ func DeleteGroup(req *restful.Request, resp *restful.Response) {
err := iam.DeleteGroup(path)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
return
}
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -106,23 +106,18 @@ func UpdateGroup(req *restful.Request, resp *restful.Response) {
}
func GroupDetail(req *restful.Request, resp *restful.Response) {
func DescribeGroup(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("path")
conn, err := ldapclient.Client()
group, err := iam.DescribeGroup(path)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
group, err := iam.GroupDetail(path, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
@@ -130,20 +125,11 @@ func GroupDetail(req *restful.Request, resp *restful.Response) {
}
func GroupUsers(req *restful.Request, resp *restful.Response) {
func ListGroupUsers(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("path")
conn, err := ldapclient.Client()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
group, err := iam.GroupDetail(path, conn)
group, err := iam.DescribeGroup(path)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
@@ -156,10 +142,10 @@ func GroupUsers(req *restful.Request, resp *restful.Response) {
for i := 0; i < len(group.Members); i++ {
name := group.Members[i]
user, err := iam.UserDetail(name, conn)
user, err := iam.DescribeUser(name)
if err != nil {
if ldap.IsErrorWithCode(err, 32) {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
group.Members = append(group.Members[:i], group.Members[i+1:]...)
i--
modify = true
@@ -170,25 +156,6 @@ func GroupUsers(req *restful.Request, resp *restful.Response) {
}
}
clusterRoles, err := iam.GetClusterRoles(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
}
if group.Path == group.Name {
workspaceRole := iam.GetWorkspaceRole(clusterRoles, group.Name)
user.WorkspaceRole = workspaceRole
}
users = append(users, user)
}
@@ -200,18 +167,7 @@ func GroupUsers(req *restful.Request, resp *restful.Response) {
}
func CountHandler(req *restful.Request, resp *restful.Response) {
count, err := iam.CountChild("")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(map[string]int{"total_count": count})
}
func RootGroupList(req *restful.Request, resp *restful.Response) {
func ListGroups(req *restful.Request, resp *restful.Response) {
array := req.QueryParameter("path")
@@ -229,18 +185,9 @@ func RootGroupList(req *restful.Request, resp *restful.Response) {
groups := make([]*models.Group, 0)
conn, err := ldapclient.Client()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
for _, v := range paths {
path := strings.TrimSpace(v)
group, err := iam.GroupDetail(path, conn)
group, err := iam.DescribeGroup(path)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return

View File

@@ -19,9 +19,9 @@ package iam
import (
"fmt"
"kubesphere.io/kubesphere/pkg/params"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/emicklei/go-restful"
@@ -31,7 +31,6 @@ import (
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
)
const (
@@ -63,7 +62,7 @@ func CreateUser(req *restful.Request, resp *restful.Response) {
return
}
err = iam.CreateUser(user)
created, err := iam.CreateUser(&user)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) {
@@ -74,7 +73,7 @@ func CreateUser(req *restful.Request, resp *restful.Response) {
return
}
resp.WriteAsJson(errors.None)
resp.WriteAsJson(created)
}
func DeleteUser(req *restful.Request, resp *restful.Response) {
@@ -100,7 +99,7 @@ func DeleteUser(req *restful.Request, resp *restful.Response) {
func UpdateUser(req *restful.Request, resp *restful.Response) {
usernameInPath := req.PathParameter("name")
username := req.HeaderParameter(constants.UserNameHeader)
usernameInHeader := req.HeaderParameter(constants.UserNameHeader)
var user models.User
err := req.ReadEntity(&user)
@@ -125,22 +124,23 @@ func UpdateUser(req *restful.Request, resp *restful.Response) {
return
}
if username == user.Username && user.Password != "" {
_, err = iam.Login(username, user.CurrentPassword, "")
// change password by self
if usernameInHeader == user.Username && user.Password != "" {
_, err = iam.Login(usernameInHeader, user.CurrentPassword, "")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("incorrect current password")))
return
}
}
err = iam.UpdateUser(user)
result, err := iam.UpdateUser(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
resp.WriteAsJson(result)
}
func UserLoginLog(req *restful.Request, resp *restful.Response) {
@@ -167,20 +167,11 @@ func UserLoginLog(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(result)
}
func CurrentUserDetail(req *restful.Request, resp *restful.Response) {
func DescribeUser(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
username := req.PathParameter("username")
conn, err := ldapclient.Client()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
user, err := iam.UserDetail(username, conn)
user, err := iam.DescribeUser(username)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
@@ -191,186 +182,81 @@ func CurrentUserDetail(req *restful.Request, resp *restful.Response) {
return
}
clusterRoles, err := iam.GetClusterRoles(username)
clusterRole, err := iam.GetUserClusterRole(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRules, err := iam.GetClusterRoleSimpleRules(clusterRoles)
user.ClusterRole = clusterRole.Name
clusterRules, err := iam.GetUserClusterSimpleRules(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
result := struct {
*models.User
ClusterRules []models.SimpleRule `json:"cluster_rules"`
}{
User: user,
ClusterRules: clusterRules,
}
user.ClusterRules = clusterRules
resp.WriteAsJson(user)
resp.WriteAsJson(result)
}
func NamespacesListHandler(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("name")
func Precheck(req *restful.Request, resp *restful.Response) {
namespaces, err := iam.GetNamespaces(username)
check := req.QueryParameter("check")
exist, err := iam.UserCreateCheck(check)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(namespaces)
resp.WriteAsJson(map[string]bool{"exist": exist})
}
func UserDetail(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("name")
usernameFromHeader := req.HeaderParameter(constants.UserNameHeader)
if username == usernameFromHeader {
CurrentUserDetail(req, resp)
return
}
conn, err := ldapclient.Client()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
user, err := iam.UserDetail(username, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRules, err := iam.GetClusterRoleSimpleRules(clusterRoles)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
workspaceRoles := iam.GetWorkspaceRoles(clusterRoles)
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
}
user.ClusterRules = clusterRules
user.WorkspaceRoles = workspaceRoles
resp.WriteAsJson(user)
}
func UserList(req *restful.Request, resp *restful.Response) {
limit, err := strconv.Atoi(req.QueryParameter("limit"))
if err != nil {
limit = 65535
}
offset, err := strconv.Atoi(req.QueryParameter("offset"))
if err != nil {
offset = 0
}
func ListUsers(req *restful.Request, resp *restful.Response) {
if check := req.QueryParameter("check"); check != "" {
exist, err := iam.UserCreateCheck(check)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(map[string]bool{"exist": exist})
Precheck(req, resp)
return
}
conn, err := ldapclient.Client()
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.ParseReverse(req)
names := params.ParseArray(req.QueryParameter(params.NameParam))
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if len(names) > 0 {
users, err := iam.ListUsersByName(names)
defer conn.Close()
if query := req.QueryParameter("name"); query != "" {
names := strings.Split(query, ",")
users := make([]*models.User, 0)
for _, name := range names {
user, err := iam.UserDetail(name, conn)
if err != nil {
if ldap.IsErrorWithCode(err, 32) {
continue
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
}
users = append(users, user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
resp.WriteAsJson(users)
return
}
var total int
var users []models.User
if query := req.QueryParameter("search"); query != "" {
total, users, err = iam.Search(query, limit, offset)
} else if query := req.QueryParameter("keyword"); query != "" {
total, users, err = iam.Search(query, limit, offset)
} else {
total, users, err = iam.UserList(limit, offset)
}
users, err := iam.ListUsers(conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for i := 0; i < len(users); i++ {
clusterRoles, err := iam.GetClusterRoles(users[i].Username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for j := 0; j < len(clusterRoles); j++ {
if clusterRoles[j].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
users[i].ClusterRole = clusterRoles[j].Name
break
}
}
}
items := make([]interface{}, 0)
for _, u := range users {
items = append(items, u)
}
resp.WriteAsJson(models.PageableResponse{Items: items, TotalCount: total})
resp.WriteAsJson(users)
}

View File

@@ -16,39 +16,3 @@
*/
package iam
import "sync"
type Counter struct {
value int
m *sync.Mutex
}
func NewCounter(value int) Counter {
c := Counter{}
c.m = &sync.Mutex{}
c.Set(value)
return c
}
func (c *Counter) Set(value int) {
c.m.Lock()
c.value = value
c.m.Unlock()
}
func (c *Counter) Add(value int) {
c.m.Lock()
c.value += value
c.m.Unlock()
}
func (c *Counter) Sub(value int) {
c.m.Lock()
c.value -= value
c.m.Unlock()
}
func (c *Counter) Get() int {
return c.value
}

View File

@@ -18,698 +18,155 @@
package iam
import (
"fmt"
"github.com/go-ldap/ldap"
"net/http"
"regexp"
"sort"
"strconv"
"strings"
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/metrics"
"kubesphere.io/kubesphere/pkg/models/workspaces"
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
sliceutils "kubesphere.io/kubesphere/pkg/utils"
"kubesphere.io/kubesphere/pkg/params"
"net/http"
)
const UserNameHeader = "X-Token-Username"
func ListWorkspaceRoles(req *restful.Request, resp *restful.Response) {
func WorkspaceRoles(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
roles, err := workspaces.Roles(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(roles)
}
func WorkspaceMemberDetail(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("member")
user, err := iam.GetUser(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
namespaces, err := workspaces.Namespaces(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.WorkspaceRole = user.WorkspaceRoles[workspace]
roles := make(map[string]string)
for _, namespace := range namespaces {
if role := user.Roles[namespace.Name]; role != "" {
roles[namespace.Name] = role
}
}
user.Roles = roles
user.Rules = nil
user.WorkspaceRules = nil
user.WorkspaceRoles = nil
user.ClusterRules = nil
resp.WriteAsJson(user)
}
func WorkspaceMemberInvite(req *restful.Request, resp *restful.Response) {
var users []models.UserInvite
workspace := req.PathParameter("name")
err := req.ReadEntity(&users)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.Invite(workspace, users)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func WorkspaceMemberRemove(req *restful.Request, resp *restful.Response) {
query := req.QueryParameter("name")
workspace := req.PathParameter("name")
names := strings.Split(query, ",")
err := workspaces.RemoveMembers(workspace, names)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func NamespaceCheckHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
exist, err := workspaces.NamespaceExistCheck(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(map[string]bool{"exist": exist})
}
func NamespaceDeleteHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
workspace := req.PathParameter("name")
err := workspaces.DeleteNamespace(workspace, namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("id")
workspace := req.PathParameter("name")
force := req.QueryParameter("force")
username := req.HeaderParameter(UserNameHeader)
err := workspaces.UnBindDevopsProject(workspace, devops)
if err != nil && force != "true" {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.DeleteDevopsProject(username, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
var devops models.DevopsProject
err := req.ReadEntity(&devops)
workspace := req.PathParameter("workspace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
project, err := workspaces.CreateDevopsProject(username, workspace, devops)
result, err := iam.ListWorkspaceRoles(workspace, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(project)
resp.WriteAsJson(result.Items)
}
func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
func ListWorkspaceRoleRules(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
role := req.PathParameter("role")
namespace := &v1.Namespace{}
err := req.ReadEntity(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if namespace.Annotations == nil {
namespace.Annotations = make(map[string]string, 0)
}
namespace.Annotations["creator"] = username
namespace.Annotations["workspace"] = workspace
if namespace.Labels == nil {
namespace.Labels = make(map[string]string, 0)
}
namespace.Labels["kubesphere.io/workspace"] = workspace
namespace, err = workspaces.CreateNamespace(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
resp.WriteAsJson(namespace)
}
func DevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
total, devOpsProjects, err := workspaces.ListDevopsProjectsByUser(username, workspace, keyword, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range devOpsProjects {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}
func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) {
var workspace models.Workspace
username := req.HeaderParameter(UserNameHeader)
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
if workspace.Admin != "" {
workspace.Creator = workspace.Admin
} else {
workspace.Creator = username
}
created, err := workspaces.Create(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(created)
}
func DeleteWorkspaceHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
if name == "" || strings.Contains(name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
workspace, err := workspaces.Detail(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.Delete(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func WorkspaceEditHandler(req *restful.Request, resp *restful.Response) {
var workspace models.Workspace
name := req.PathParameter("name")
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if name != workspace.Name {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("the name of workspace (%s) does not match the name on the URL (%s)", workspace.Name, name)))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
edited, err := workspaces.Edit(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(edited)
}
func WorkspaceDetailHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(workspace)
}
// List all workspaces for the current user
func UserWorkspaceListHandler(req *restful.Request, resp *restful.Response) {
keyword := req.QueryParameter("keyword")
username := req.HeaderParameter(constants.UserNameHeader)
ws, err := workspaces.ListWorkspaceByUser(username, keyword)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
sort.Slice(ws, func(i, j int) bool {
t1, err := ws[i].GetCreateTime()
if err != nil {
return false
}
t2, err := ws[j].GetCreateTime()
if err != nil {
return true
}
return t1.After(t2)
})
resp.WriteAsJson(ws)
}
func UserNamespaceListHandler(req *restful.Request, resp *restful.Response) {
withMetrics, err := strconv.ParseBool(req.QueryParameter("metrics"))
if err != nil {
withMetrics = false
}
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
if page < 0 {
page = 1
}
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
workspaceName := req.PathParameter("workspace")
total, namespaces, err := workspaces.ListNamespaceByUser(workspaceName, username, keyword, orderBy, reverse, limit, offset)
if withMetrics {
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range namespaces {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}
func DevopsRulesHandler(req *restful.Request, resp *restful.Response) {
//workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
devopsName := req.PathParameter("devops")
var rules []models.SimpleRule
role, err := iam.GetDevopsRole(devopsName, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
switch role {
case "developer":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"view", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
}
break
case "owner":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "devops", Actions: []string{"edit", "view", "delete"}},
}
break
case "maintainer":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "devops", Actions: []string{"view"}},
}
break
case "reporter":
fallthrough
default:
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"view"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
}
break
}
rules := iam.GetWorkspaceRoleSimpleRules(workspace, role)
resp.WriteAsJson(rules)
}
func NamespacesRulesHandler(req *restful.Request, resp *restful.Response) {
//workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
namespaceName := req.PathParameter("namespace")
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
roles, err := iam.GetRoles(username, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for _, clusterRole := range clusterRoles {
role := new(rbac.Role)
role.Name = clusterRole.Name
role.Labels = clusterRole.Labels
role.Namespace = namespaceName
role.Annotations = clusterRole.Annotations
role.Kind = "Role"
role.Rules = clusterRole.Rules
roles = append(roles, role)
}
rules, err := iam.GetRoleSimpleRules(roles, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
if rules[namespaceName] == nil {
resp.WriteAsJson(make([]models.SimpleRule, 0))
} else {
resp.WriteAsJson(rules[namespaceName])
}
}
func WorkspaceRulesHandler(req *restful.Request, resp *restful.Response) {
func DescribeWorkspaceRole(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
roleName := req.PathParameter("role")
username := req.HeaderParameter(constants.UserNameHeader)
clusterRoles, err := iam.GetClusterRoles(username)
role, err := iam.GetWorkspaceRole(workspace, roleName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
rules := iam.GetWorkspaceSimpleRules(clusterRoles, workspace)
if rules[workspace] != nil {
resp.WriteAsJson(rules[workspace])
} else if rules["*"] != nil {
resp.WriteAsJson(rules["*"])
} else {
resp.WriteAsJson(make([]models.SimpleRule, 0))
}
resp.WriteAsJson(role)
}
func WorkspaceMemberList(req *restful.Request, resp *restful.Response) {
func DescribeWorkspaceUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
limit, err := strconv.Atoi(req.QueryParameter("limit"))
username := req.PathParameter("username")
workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username)
if err != nil {
limit = 500
}
offset, err := strconv.Atoi(req.QueryParameter("offset"))
if err != nil {
offset = 0
if k8serr.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
conn, err := ldapclient.Client()
user, err := iam.DescribeUser(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
user.WorkspaceRole = workspaceRole.Labels[constants.DisplayNameLabelKey]
group, err := iam.GroupDetail(workspace, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
keyword := ""
if query := req.QueryParameter("keyword"); query != "" {
keyword = query
}
users := make([]*models.User, 0)
total := len(group.Members)
members := sliceutils.RemoveString(group.Members, func(item string) bool {
return keyword != "" && !strings.Contains(item, keyword)
})
for i := 0; i < len(members); i++ {
username := members[i]
if i < offset {
continue
}
if len(users) == limit {
break
}
user, err := iam.UserDetail(username, conn)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
group.Members = sliceutils.RemoveString(group.Members, func(item string) bool {
return item == username
})
continue
} else {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
}
clusterRoles, err := iam.GetClusterRoles(username)
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
}
if group.Path == group.Name {
workspaceRole := iam.GetWorkspaceRole(clusterRoles, group.Name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.WorkspaceRole = workspaceRole
}
users = append(users, user)
}
if total > len(group.Members) {
go iam.UpdateGroup(group)
}
if req.QueryParameter("limit") != "" {
resp.WriteAsJson(map[string]interface{}{"items": users, "total_count": len(members)})
} else {
resp.WriteAsJson(users)
}
resp.WriteAsJson(user)
}
func ListDevopsRoleRules(req *restful.Request, resp *restful.Response) {
role := req.PathParameter("role")
rules := iam.GetDevopsRoleSimpleRules(role)
resp.WriteAsJson(rules)
}
func InviteUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
var user models.User
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = workspaces.InviteUser(workspace, &user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func RemoveUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
var user models.User
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = workspaces.InviteUser(workspace, &user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func ListWorkspaceUsers(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := iam.ListWorkspaceUsers(workspace, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}

View File

@@ -21,15 +21,17 @@ import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/applications"
//"kubesphere.io/kubesphere/pkg/models/applications"
"kubesphere.io/kubesphere/pkg/params"
"net/http"
)
func ApplicationHandler(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
clusterId := req.QueryParameter("cluster_id")
runtimeId := req.QueryParameter("runtime_id")
conditions, err := params.ParseConditions(req)
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
if err != nil {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))

View File

@@ -1,44 +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 resources
import (
"github.com/emicklei/go-restful"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
)
func ClusterResourceHandler(req *restful.Request, resp *restful.Response) {
resourceName := req.PathParameter("resources")
conditions, err := params.ParseConditions(req)
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req)
reverse := params.ParseReverse(req)
result, err := resources.ListClusterResource(resourceName, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}

View File

@@ -19,22 +19,39 @@ package resources
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/resources"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
)
func NamespaceResourceHandler(req *restful.Request, resp *restful.Response) {
func ListResources(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
resourceName := req.PathParameter("resources")
conditions, err := params.ParseConditions(req)
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
names := params.ParseArray(req.QueryParameter(params.NameParam))
result, err := resources.ListNamespaceResource(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
if orderBy == "" {
orderBy = resources.CreateTime
reverse = true
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
var result *models.PageableResponse
if len(names) > 0 {
result, err = resources.ListResourcesByName(namespace, resourceName, names)
} else {
result, err = resources.ListResources(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))

View File

@@ -50,21 +50,6 @@ func GetAllRouters(request *restful.Request, response *restful.Response) {
response.WriteAsJson(routers)
}
// Get all namespace ingress controller services for user
func GetAllRoutersOfUser(request *restful.Request, response *restful.Response) {
username := request.PathParameter("username")
routers, err := routers.GetAllRoutersOfUser(username)
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
response.WriteAsJson(routers)
}
// Get ingress controller service for specified namespace
func GetRouter(request *restful.Request, response *restful.Response) {

View File

@@ -0,0 +1,264 @@
/*
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 tenant
import (
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/tenant"
"kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/params"
"log"
"net/http"
)
func ListWorkspaceRules(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(rules)
}
func ListWorkspaces(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func ListNamespaces(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("username")
// /workspaces/{workspace}/members/{username}/namespaces
if username == "" {
// /workspaces/{workspace}/namespaces
username = req.HeaderParameter(constants.UserNameHeader)
}
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
conditions.Match["kubesphere.io/workspace"] = workspace
result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func CreateNamespace(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var namespace v1.Namespace
err := req.ReadEntity(&namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
workspace, err := tenant.GetWorkspace(workspaceName)
err = checkResourceQuotas(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
return
}
if err != nil {
if k8serr.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
created, err := tenant.CreateNamespace(workspaceName, &namespace, username)
if err != nil {
if k8serr.IsAlreadyExists(err) {
resp.WriteHeaderAndEntity(http.StatusConflict, err)
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, err)
}
return
}
resp.WriteAsJson(created)
}
func DeleteNamespace(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
namespaceName := req.PathParameter("namespace")
err := workspaces.DeleteNamespace(workspaceName, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func checkResourceQuotas(wokrspace *v1alpha1.Workspace) error {
return nil
}
func ListDevopsProjects(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter(constants.UserNameHeader)
if username == "" {
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.ParseReverse(req)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := tenant.ListDevopsProjects(workspace, username, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("id")
workspace := req.PathParameter("workspace")
force := req.QueryParameter("force")
username := req.HeaderParameter(constants.UserNameHeader)
err := workspaces.UnBindDevopsProject(workspace, devops)
if err != nil && force != "true" {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.DeleteDevopsProject(username, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var devops models.DevopsProject
err := req.ReadEntity(&devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
log.Println("create workspace", username, workspace, devops)
project, err := workspaces.CreateDevopsProject(username, workspace, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(project)
}
func ListNamespaceRules(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserNamespaceSimpleRules(namespace, username)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules)
}
func ListDevopsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserDevopsSimpleRules(username, devops)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules)
}

View File

@@ -0,0 +1,56 @@
/*
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 terminal
import (
"github.com/emicklei/go-restful"
"gopkg.in/igm/sockjs-go.v2/sockjs"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/terminal"
"net/http"
)
// TerminalResponse is sent by handleExecShell. The Id is a random session id that binds the original REST request and the SockJS connection.
// Any clientapi in possession of this Id can hijack the terminal session.
type TerminalResponse struct {
Id string `json:"id"`
}
// CreateAttachHandler is called from main for /api/sockjs
func NewTerminalHandler(path string) http.Handler {
return sockjs.NewHandler(path, sockjs.DefaultOptions, terminal.HandleTerminalSession)
}
// Handles execute shell API call
func CreateTerminalSession(request *restful.Request, resp *restful.Response) {
namespace := request.PathParameter("namespace")
podName := request.PathParameter("pod")
containerName := request.QueryParameter("container")
shell := request.QueryParameter("shell")
sessionId, err := terminal.NewSession(shell, namespace, podName, containerName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
TerminalResponse := &TerminalResponse{Id: sessionId}
resp.WriteAsJson(TerminalResponse)
}

View File

@@ -19,10 +19,11 @@ limitations under the License.
package versioned
import (
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/flowcontrol"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1"
)
type Interface interface {
@@ -30,6 +31,9 @@ type Interface interface {
ServicemeshV1alpha2() servicemeshv1alpha2.ServicemeshV1alpha2Interface
// Deprecated: please explicitly pick a version if possible.
Servicemesh() servicemeshv1alpha2.ServicemeshV1alpha2Interface
TenantV1alpha1() tenantv1alpha1.TenantV1alpha1Interface
// Deprecated: please explicitly pick a version if possible.
Tenant() tenantv1alpha1.TenantV1alpha1Interface
}
// Clientset contains the clients for groups. Each group has exactly one
@@ -37,6 +41,7 @@ type Interface interface {
type Clientset struct {
*discovery.DiscoveryClient
servicemeshV1alpha2 *servicemeshv1alpha2.ServicemeshV1alpha2Client
tenantV1alpha1 *tenantv1alpha1.TenantV1alpha1Client
}
// ServicemeshV1alpha2 retrieves the ServicemeshV1alpha2Client
@@ -50,6 +55,17 @@ func (c *Clientset) Servicemesh() servicemeshv1alpha2.ServicemeshV1alpha2Interfa
return c.servicemeshV1alpha2
}
// TenantV1alpha1 retrieves the TenantV1alpha1Client
func (c *Clientset) TenantV1alpha1() tenantv1alpha1.TenantV1alpha1Interface {
return c.tenantV1alpha1
}
// Deprecated: Tenant retrieves the default version of TenantClient.
// Please explicitly pick a version.
func (c *Clientset) Tenant() tenantv1alpha1.TenantV1alpha1Interface {
return c.tenantV1alpha1
}
// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
if c == nil {
@@ -70,6 +86,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
if err != nil {
return nil, err
}
cs.tenantV1alpha1, err = tenantv1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil {
@@ -83,6 +103,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.servicemeshV1alpha2 = servicemeshv1alpha2.NewForConfigOrDie(c)
cs.tenantV1alpha1 = tenantv1alpha1.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &cs
@@ -92,6 +113,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
func New(c rest.Interface) *Clientset {
var cs Clientset
cs.servicemeshV1alpha2 = servicemeshv1alpha2.New(c)
cs.tenantV1alpha1 = tenantv1alpha1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs

View File

@@ -27,6 +27,8 @@ import (
clientset "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2"
fakeservicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2/fake"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1"
faketenantv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1/fake"
)
// NewSimpleClientset returns a clientset that will respond with the provided objects.
@@ -80,3 +82,13 @@ func (c *Clientset) ServicemeshV1alpha2() servicemeshv1alpha2.ServicemeshV1alpha
func (c *Clientset) Servicemesh() servicemeshv1alpha2.ServicemeshV1alpha2Interface {
return &fakeservicemeshv1alpha2.FakeServicemeshV1alpha2{Fake: &c.Fake}
}
// TenantV1alpha1 retrieves the TenantV1alpha1Client
func (c *Clientset) TenantV1alpha1() tenantv1alpha1.TenantV1alpha1Interface {
return &faketenantv1alpha1.FakeTenantV1alpha1{Fake: &c.Fake}
}
// Tenant retrieves the TenantV1alpha1Client
func (c *Clientset) Tenant() tenantv1alpha1.TenantV1alpha1Interface {
return &faketenantv1alpha1.FakeTenantV1alpha1{Fake: &c.Fake}
}

View File

@@ -19,12 +19,13 @@ limitations under the License.
package fake
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
)
var scheme = runtime.NewScheme()
@@ -32,6 +33,7 @@ var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
servicemeshv1alpha2.AddToScheme,
tenantv1alpha1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition

View File

@@ -19,12 +19,13 @@ limitations under the License.
package scheme
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
)
var Scheme = runtime.NewScheme()
@@ -32,6 +33,7 @@ var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
servicemeshv1alpha2.AddToScheme,
tenantv1alpha1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition

View File

@@ -0,0 +1,20 @@
/*
Copyright The Kubernetes 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 v1alpha1

View File

@@ -0,0 +1,20 @@
/*
Copyright The Kubernetes 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

View File

@@ -0,0 +1,40 @@
/*
Copyright The Kubernetes 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 (
"k8s.io/client-go/rest"
"k8s.io/client-go/testing"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1"
)
type FakeTenantV1alpha1 struct {
*testing.Fake
}
func (c *FakeTenantV1alpha1) Workspaces() v1alpha1.WorkspaceInterface {
return &FakeWorkspaces{c}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeTenantV1alpha1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@@ -0,0 +1,131 @@
/*
Copyright The Kubernetes 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 (
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/testing"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
)
// FakeWorkspaces implements WorkspaceInterface
type FakeWorkspaces struct {
Fake *FakeTenantV1alpha1
}
var workspacesResource = schema.GroupVersionResource{Group: "tenant.kubesphere.io", Version: "v1alpha1", Resource: "workspaces"}
var workspacesKind = schema.GroupVersionKind{Group: "tenant.kubesphere.io", Version: "v1alpha1", Kind: "Workspace"}
// Get takes name of the workspace, and returns the corresponding workspace object, and an error if there is any.
func (c *FakeWorkspaces) Get(name string, options v1.GetOptions) (result *v1alpha1.Workspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootGetAction(workspacesResource, name), &v1alpha1.Workspace{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Workspace), err
}
// List takes label and field selectors, and returns the list of Workspaces that match those selectors.
func (c *FakeWorkspaces) List(opts v1.ListOptions) (result *v1alpha1.WorkspaceList, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootListAction(workspacesResource, workspacesKind, opts), &v1alpha1.WorkspaceList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.WorkspaceList{ListMeta: obj.(*v1alpha1.WorkspaceList).ListMeta}
for _, item := range obj.(*v1alpha1.WorkspaceList).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 workspaces.
func (c *FakeWorkspaces) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewRootWatchAction(workspacesResource, opts))
}
// Create takes the representation of a workspace and creates it. Returns the server's representation of the workspace, and an error, if there is any.
func (c *FakeWorkspaces) Create(workspace *v1alpha1.Workspace) (result *v1alpha1.Workspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootCreateAction(workspacesResource, workspace), &v1alpha1.Workspace{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Workspace), err
}
// Update takes the representation of a workspace and updates it. Returns the server's representation of the workspace, and an error, if there is any.
func (c *FakeWorkspaces) Update(workspace *v1alpha1.Workspace) (result *v1alpha1.Workspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateAction(workspacesResource, workspace), &v1alpha1.Workspace{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Workspace), 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 *FakeWorkspaces) UpdateStatus(workspace *v1alpha1.Workspace) (*v1alpha1.Workspace, error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceAction(workspacesResource, "status", workspace), &v1alpha1.Workspace{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Workspace), err
}
// Delete takes name of the workspace and deletes it. Returns an error if one occurs.
func (c *FakeWorkspaces) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewRootDeleteAction(workspacesResource, name), &v1alpha1.Workspace{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeWorkspaces) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewRootDeleteCollectionAction(workspacesResource, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.WorkspaceList{})
return err
}
// Patch applies the patch and returns the patched workspace.
func (c *FakeWorkspaces) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Workspace, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootPatchSubresourceAction(workspacesResource, name, pt, data, subresources...), &v1alpha1.Workspace{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Workspace), err
}

View File

@@ -0,0 +1,21 @@
/*
Copyright The Kubernetes 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 v1alpha1
type WorkspaceExpansion interface{}

View File

@@ -0,0 +1,90 @@
/*
Copyright The Kubernetes 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 v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
)
type TenantV1alpha1Interface interface {
RESTClient() rest.Interface
WorkspacesGetter
}
// TenantV1alpha1Client is used to interact with features provided by the tenant.kubesphere.io group.
type TenantV1alpha1Client struct {
restClient rest.Interface
}
func (c *TenantV1alpha1Client) Workspaces() WorkspaceInterface {
return newWorkspaces(c)
}
// NewForConfig creates a new TenantV1alpha1Client for the given config.
func NewForConfig(c *rest.Config) (*TenantV1alpha1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &TenantV1alpha1Client{client}, nil
}
// NewForConfigOrDie creates a new TenantV1alpha1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *TenantV1alpha1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new TenantV1alpha1Client for the given RESTClient.
func New(c rest.Interface) *TenantV1alpha1Client {
return &TenantV1alpha1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1alpha1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
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 *TenantV1alpha1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View File

@@ -0,0 +1,180 @@
/*
Copyright The Kubernetes 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 v1alpha1
import (
"time"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
)
// WorkspacesGetter has a method to return a WorkspaceInterface.
// A group's client should implement this interface.
type WorkspacesGetter interface {
Workspaces() WorkspaceInterface
}
// WorkspaceInterface has methods to work with Workspace resources.
type WorkspaceInterface interface {
Create(*v1alpha1.Workspace) (*v1alpha1.Workspace, error)
Update(*v1alpha1.Workspace) (*v1alpha1.Workspace, error)
UpdateStatus(*v1alpha1.Workspace) (*v1alpha1.Workspace, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.Workspace, error)
List(opts v1.ListOptions) (*v1alpha1.WorkspaceList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Workspace, err error)
WorkspaceExpansion
}
// workspaces implements WorkspaceInterface
type workspaces struct {
client rest.Interface
}
// newWorkspaces returns a Workspaces
func newWorkspaces(c *TenantV1alpha1Client) *workspaces {
return &workspaces{
client: c.RESTClient(),
}
}
// Get takes name of the workspace, and returns the corresponding workspace object, and an error if there is any.
func (c *workspaces) Get(name string, options v1.GetOptions) (result *v1alpha1.Workspace, err error) {
result = &v1alpha1.Workspace{}
err = c.client.Get().
Resource("workspaces").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Workspaces that match those selectors.
func (c *workspaces) List(opts v1.ListOptions) (result *v1alpha1.WorkspaceList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.WorkspaceList{}
err = c.client.Get().
Resource("workspaces").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested workspaces.
func (c *workspaces) 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("workspaces").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a workspace and creates it. Returns the server's representation of the workspace, and an error, if there is any.
func (c *workspaces) Create(workspace *v1alpha1.Workspace) (result *v1alpha1.Workspace, err error) {
result = &v1alpha1.Workspace{}
err = c.client.Post().
Resource("workspaces").
Body(workspace).
Do().
Into(result)
return
}
// Update takes the representation of a workspace and updates it. Returns the server's representation of the workspace, and an error, if there is any.
func (c *workspaces) Update(workspace *v1alpha1.Workspace) (result *v1alpha1.Workspace, err error) {
result = &v1alpha1.Workspace{}
err = c.client.Put().
Resource("workspaces").
Name(workspace.Name).
Body(workspace).
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 *workspaces) UpdateStatus(workspace *v1alpha1.Workspace) (result *v1alpha1.Workspace, err error) {
result = &v1alpha1.Workspace{}
err = c.client.Put().
Resource("workspaces").
Name(workspace.Name).
SubResource("status").
Body(workspace).
Do().
Into(result)
return
}
// Delete takes name of the workspace and deletes it. Returns an error if one occurs.
func (c *workspaces) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Resource("workspaces").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *workspaces) 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("workspaces").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched workspace.
func (c *workspaces) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Workspace, err error) {
result = &v1alpha1.Workspace{}
err = c.client.Patch(pt).
Resource("workspaces").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@@ -19,17 +19,18 @@ limitations under the License.
package externalversions
import (
reflect "reflect"
sync "sync"
time "time"
"reflect"
"sync"
"time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
servicemesh "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant"
)
// SharedInformerOption defines the functional option type for SharedInformerFactory.
@@ -173,8 +174,13 @@ type SharedInformerFactory interface {
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
Servicemesh() servicemesh.Interface
Tenant() tenant.Interface
}
func (f *sharedInformerFactory) Servicemesh() servicemesh.Interface {
return servicemesh.New(f, f.namespace, f.tweakListOptions)
}
func (f *sharedInformerFactory) Tenant() tenant.Interface {
return tenant.New(f, f.namespace, f.tweakListOptions)
}

View File

@@ -21,9 +21,10 @@ package externalversions
import (
"fmt"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache"
"kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
)
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
@@ -58,6 +59,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
case v1alpha2.SchemeGroupVersion.WithResource("strategies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().Strategies().Informer()}, nil
// Group=tenant.kubesphere.io, Version=v1alpha1
case v1alpha1.SchemeGroupVersion.WithResource("workspaces"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Tenant().V1alpha1().Workspaces().Informer()}, nil
}
return nil, fmt.Errorf("no informer found for %v", resource)

View File

@@ -0,0 +1,46 @@
/*
Copyright The Kubernetes 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 tenant
import (
"kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
)
// Interface provides access to each of this group's versions.
type Interface interface {
// V1alpha1 provides access to shared informers for resources in V1alpha1.
V1alpha1() v1alpha1.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}
}
// V1alpha1 returns a new v1alpha1.Interface.
func (g *group) V1alpha1() v1alpha1.Interface {
return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
}

View File

@@ -0,0 +1,45 @@
/*
Copyright The Kubernetes 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 v1alpha1
import (
"kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// Workspaces returns a WorkspaceInformer.
Workspaces() WorkspaceInformer
}
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}
}
// Workspaces returns a WorkspaceInformer.
func (v *version) Workspaces() WorkspaceInformer {
return &workspaceInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
}

View File

@@ -0,0 +1,88 @@
/*
Copyright The Kubernetes 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 v1alpha1
import (
"time"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
"kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha1"
)
// WorkspaceInformer provides access to a shared informer and lister for
// Workspaces.
type WorkspaceInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.WorkspaceLister
}
type workspaceInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// NewWorkspaceInformer constructs a new informer for Workspace 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 NewWorkspaceInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredWorkspaceInformer(client, resyncPeriod, indexers, nil)
}
// NewFilteredWorkspaceInformer constructs a new informer for Workspace 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 NewFilteredWorkspaceInformer(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.TenantV1alpha1().Workspaces().List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.TenantV1alpha1().Workspaces().Watch(options)
},
},
&tenantv1alpha1.Workspace{},
resyncPeriod,
indexers,
)
}
func (f *workspaceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredWorkspaceInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *workspaceInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&tenantv1alpha1.Workspace{}, f.defaultInformer)
}
func (f *workspaceInformer) Lister() v1alpha1.WorkspaceLister {
return v1alpha1.NewWorkspaceLister(f.Informer().GetIndexer())
}

View File

@@ -0,0 +1,23 @@
/*
Copyright The Kubernetes 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 v1alpha1
// WorkspaceListerExpansion allows custom methods to be added to
// WorkspaceLister.
type WorkspaceListerExpansion interface{}

View File

@@ -0,0 +1,65 @@
/*
Copyright The Kubernetes 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 v1alpha1
import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
)
// WorkspaceLister helps list Workspaces.
type WorkspaceLister interface {
// List lists all Workspaces in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.Workspace, err error)
// Get retrieves the Workspace from the index for a given name.
Get(name string) (*v1alpha1.Workspace, error)
WorkspaceListerExpansion
}
// workspaceLister implements the WorkspaceLister interface.
type workspaceLister struct {
indexer cache.Indexer
}
// NewWorkspaceLister returns a new WorkspaceLister.
func NewWorkspaceLister(indexer cache.Indexer) WorkspaceLister {
return &workspaceLister{indexer: indexer}
}
// List lists all Workspaces in the indexer.
func (s *workspaceLister) List(selector labels.Selector) (ret []*v1alpha1.Workspace, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.Workspace))
})
return ret, err
}
// Get retrieves the Workspace from the index for a given name.
func (s *workspaceLister) Get(name string) (*v1alpha1.Workspace, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("workspace"), name)
}
return obj.(*v1alpha1.Workspace), nil
}

View File

@@ -31,13 +31,16 @@ const (
IngressControllerFolder = DataHome + "/ingress-controller"
IngressControllerPrefix = "kubesphere-router-"
WorkspaceLabelKey = "kubesphere.io/workspace"
WorkspaceAdmin = "workspace-admin"
ClusterAdmin = "cluster-admin"
WorkspaceRegular = "workspace-regular"
WorkspaceViewer = "workspace-viewer"
DevopsOwner = "owner"
DevopsReporter = "reporter"
WorkspaceLabelKey = "kubesphere.io/workspace"
DisplayNameLabelKey = "displayName"
CreatorLabelKey = "creator"
OpenPitrixRuntimeAnnotationKey = "openpitrix_runtime"
WorkspaceAdmin = "workspace-admin"
ClusterAdmin = "cluster-admin"
WorkspaceRegular = "workspace-regular"
WorkspaceViewer = "workspace-viewer"
DevopsOwner = "owner"
DevopsReporter = "reporter"
UserNameHeader = "X-Token-Username"
)

View File

@@ -0,0 +1,28 @@
/*
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 controller
import (
"kubesphere.io/kubesphere/pkg/controller/clusterrolebinding"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, clusterrolebinding.Add)
}

View File

@@ -0,0 +1,28 @@
/*
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 controller
import (
"kubesphere.io/kubesphere/pkg/controller/namespace"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, namespace.Add)
}

View 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 controller
import "kubesphere.io/kubesphere/pkg/controller/workspace"
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, workspace.Add)
}

View File

@@ -0,0 +1,220 @@
/*
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 clusterrolebinding
import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"reflect"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var (
log = logf.Log.WithName("controller")
)
/**
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
* business logic. Delete these comments after modifying this file.*
*/
// Add creates a new Namespace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileClusterRoleBinding{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("clusterrolebinding-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Namespace
err = c.Watch(&source.Kind{Type: &rbac.ClusterRoleBinding{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileClusterRoleBinding{}
// ReconcileClusterRoleBinding reconciles a Namespace object
type ReconcileClusterRoleBinding struct {
client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a Namespace object and makes changes based on the state read
// and what is in the Namespace.Spec
// +kubebuilder:rbac:groups=core.kubesphere.io,resources=namespaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core.kubesphere.io,resources=namespaces/status,verbs=get;update;patch
func (r *ReconcileClusterRoleBinding) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Namespace instance
instance := &rbac.ClusterRoleBinding{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
workspaceName := instance.Labels[constants.WorkspaceLabelKey]
if workspaceName != "" && k8sutil.IsControlledBy(instance.OwnerReferences, "Workspace", workspaceName) {
if instance.Name == getWorkspaceAdminRoleBindingName(workspaceName) ||
instance.Name == getWorkspaceViewerRoleBindingName(workspaceName) {
nsList := &corev1.NamespaceList{}
options := client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspaceName})}
err = r.List(context.TODO(), &options, nsList)
if err != nil {
return reconcile.Result{}, err
}
for _, ns := range nsList.Items {
err = r.updateRoleBindings(instance, &ns)
if err != nil {
return reconcile.Result{}, err
}
}
}
}
return reconcile.Result{}, nil
}
func (r *ReconcileClusterRoleBinding) updateRoleBindings(clusterRoleBinding *rbac.ClusterRoleBinding, namespace *corev1.Namespace) error {
workspaceName := namespace.Labels[constants.WorkspaceLabelKey]
if clusterRoleBinding.Name == getWorkspaceAdminRoleBindingName(workspaceName) {
adminBinding := &rbac.RoleBinding{}
adminBinding.Name = "admin"
adminBinding.Namespace = namespace.Name
adminBinding.RoleRef = rbac.RoleRef{Name: "admin", APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
adminBinding.Subjects = clusterRoleBinding.Subjects
found := &rbac.RoleBinding{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: adminBinding.Name}, found)
if errors.IsNotFound(err) {
log.Info("Creating default role binding", "namespace", namespace.Name, "name", adminBinding.Name)
err = r.Create(context.TODO(), adminBinding)
if err != nil {
return err
}
} else if err != nil {
return err
}
if !reflect.DeepEqual(found.RoleRef, adminBinding.RoleRef) {
log.Info("Deleting conflict role binding", "namespace", namespace.Name, "name", adminBinding.Name)
err = r.Delete(context.TODO(), found)
if err != nil {
return err
}
return fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, adminBinding.Name)
}
if !reflect.DeepEqual(found.Subjects, adminBinding.Subjects) {
found.Subjects = adminBinding.Subjects
log.Info("Updating role binding", "namespace", namespace.Name, "name", adminBinding.Name)
err = r.Update(context.TODO(), found)
if err != nil {
return err
}
}
}
if clusterRoleBinding.Name == getWorkspaceViewerRoleBindingName(workspaceName) {
found := &rbac.RoleBinding{}
viewerBinding := &rbac.RoleBinding{}
viewerBinding.Name = "viewer"
viewerBinding.Namespace = namespace.Name
viewerBinding.RoleRef = rbac.RoleRef{Name: "viewer", APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
viewerBinding.Subjects = clusterRoleBinding.Subjects
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: viewerBinding.Name}, found)
if errors.IsNotFound(err) {
log.Info("Creating default role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
err = r.Create(context.TODO(), viewerBinding)
if err != nil {
return err
}
} else if err != nil {
return err
}
if !reflect.DeepEqual(found.RoleRef, viewerBinding.RoleRef) {
log.Info("Deleting conflict role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
err = r.Delete(context.TODO(), found)
if err != nil {
return err
}
return fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, viewerBinding.Name)
}
if !reflect.DeepEqual(found.Subjects, viewerBinding.Subjects) {
found.Subjects = viewerBinding.Subjects
log.Info("Updating role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
err = r.Update(context.TODO(), found)
if err != nil {
return err
}
}
}
return nil
}
func getWorkspaceAdminRoleBindingName(workspaceName string) string {
return fmt.Sprintf("workspace:%s:admin", workspaceName)
}
func getWorkspaceViewerRoleBindingName(workspaceName string) string {
return fmt.Sprintf("workspace:%s:viewer", workspaceName)
}

View File

@@ -0,0 +1,77 @@
/*
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 clusterrolebinding
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -0,0 +1,19 @@
/*
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 clusterrolebinding

View File

@@ -0,0 +1,436 @@
/*
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 namespace
import (
"context"
"fmt"
"github.com/golang/glog"
corev1 "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
"k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/apis/core"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"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"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var (
log = logf.Log.WithName("controller")
defaultRoles = []rbac.Role{
{ObjectMeta: metav1.ObjectMeta{Name: "admin"}, Rules: []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}},
{ObjectMeta: metav1.ObjectMeta{Name: "operator"}, 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", "resources.kubesphere.io", "autoscaling"}, Resources: []string{"*"}}}},
{ObjectMeta: metav1.ObjectMeta{Name: "viewer"}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}},
}
)
/**
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
* business logic. Delete these comments after modifying this file.*
*/
// Add creates a new Namespace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileNamespace{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("namespace-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Namespace
err = c.Watch(&source.Kind{Type: &corev1.Namespace{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileNamespace{}
// ReconcileNamespace reconciles a Namespace object
type ReconcileNamespace struct {
client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a Namespace object and makes changes based on the state read
// and what is in the Namespace.Spec
// +kubebuilder:rbac:groups=core.kubesphere.io,resources=namespaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core.kubesphere.io,resources=namespaces/status,verbs=get;update;patch
func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Namespace instance
instance := &corev1.Namespace{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
if !instance.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is being deleted
if err := r.deleteRuntime(instance); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
workspaceName := instance.Labels[constants.WorkspaceLabelKey]
// delete default role bindings
if workspaceName == "" {
adminBinding := &rbac.RoleBinding{}
adminBinding.Name = "admin"
adminBinding.Namespace = instance.Name
log.Info("Deleting default role binding", "namespace", instance.Name, "name", adminBinding.Name)
err := r.Delete(context.TODO(), adminBinding)
if err != nil && !errors.IsNotFound(err) {
return reconcile.Result{}, err
}
viewerBinding := &rbac.RoleBinding{}
viewerBinding.Name = "viewer"
viewerBinding.Namespace = instance.Name
log.Info("Deleting default role binding", "namespace", instance.Name, "name", viewerBinding.Name)
err = r.Delete(context.TODO(), viewerBinding)
if err != nil && !errors.IsNotFound(err) {
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
if err = r.checkAndBindWorkspace(instance); err != nil {
return reconcile.Result{}, err
}
if err = r.checkAndCreateRoles(instance); err != nil {
return reconcile.Result{}, err
}
if err = r.checkAndCreateRoleBindings(instance); err != nil {
return reconcile.Result{}, err
}
if err = r.checkAndCreateCephSecret(instance); err != nil {
return reconcile.Result{}, err
}
if err := r.checkAndCreateRuntime(instance); err != nil {
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
// Create default roles
func (r *ReconcileNamespace) checkAndCreateRoles(namespace *corev1.Namespace) error {
for _, role := range defaultRoles {
found := &rbac.Role{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: role.Name}, found)
if err != nil {
if errors.IsNotFound(err) {
role := role.DeepCopy()
role.Namespace = namespace.Name
log.Info("Creating default role", "namespace", namespace.Name, "role", role.Name)
err = r.Create(context.TODO(), role)
if err != nil {
return err
}
}
return err
}
}
return nil
}
func (r *ReconcileNamespace) checkAndCreateRoleBindings(namespace *corev1.Namespace) error {
workspaceName := namespace.Labels[constants.WorkspaceLabelKey]
creatorName := namespace.Labels[constants.CreatorLabelKey]
creator := rbac.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: creatorName}
workspaceAdminBinding := &rbac.ClusterRoleBinding{}
err := r.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf("workspace:%s:admin", workspaceName)}, workspaceAdminBinding)
if err != nil {
return err
}
adminBinding := &rbac.RoleBinding{}
adminBinding.Name = "admin"
adminBinding.Namespace = namespace.Name
adminBinding.RoleRef = rbac.RoleRef{Name: "admin", APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
adminBinding.Subjects = workspaceAdminBinding.Subjects
if creator.Name != "" {
if adminBinding.Subjects == nil {
adminBinding.Subjects = make([]rbac.Subject, 0)
}
if !k8sutil.ContainsUser(adminBinding.Subjects, creatorName) {
adminBinding.Subjects = append(adminBinding.Subjects, creator)
}
}
found := &rbac.RoleBinding{}
err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: adminBinding.Name}, found)
if errors.IsNotFound(err) {
log.Info("Creating default role binding", "namespace", namespace.Name, "name", adminBinding.Name)
err = r.Create(context.TODO(), adminBinding)
if err != nil {
return err
}
} else if err != nil {
return err
}
if !reflect.DeepEqual(found.RoleRef, adminBinding.RoleRef) {
log.Info("Deleting conflict role binding", "namespace", namespace.Name, "name", adminBinding.Name)
err = r.Delete(context.TODO(), found)
if err != nil {
return err
}
return fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, adminBinding.Name)
}
if !reflect.DeepEqual(found.Subjects, adminBinding.Subjects) {
found.Subjects = adminBinding.Subjects
log.Info("Updating role binding", "namespace", namespace.Name, "name", adminBinding.Name)
err = r.Update(context.TODO(), found)
if err != nil {
return err
}
}
workspaceViewerBinding := &rbac.ClusterRoleBinding{}
err = r.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf("workspace:%s:viewer", workspaceName)}, workspaceViewerBinding)
if err != nil {
return err
}
viewerBinding := &rbac.RoleBinding{}
viewerBinding.Name = "viewer"
viewerBinding.Namespace = namespace.Name
viewerBinding.RoleRef = rbac.RoleRef{Name: "viewer", APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
viewerBinding.Subjects = workspaceViewerBinding.Subjects
err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: viewerBinding.Name}, found)
if errors.IsNotFound(err) {
log.Info("Creating default role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
err = r.Create(context.TODO(), viewerBinding)
if err != nil {
return err
}
} else if err != nil {
return err
}
if !reflect.DeepEqual(found.RoleRef, viewerBinding.RoleRef) {
log.Info("Deleting conflict role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
err = r.Delete(context.TODO(), found)
if err != nil {
return err
}
return fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, viewerBinding.Name)
}
if !reflect.DeepEqual(found.Subjects, viewerBinding.Subjects) {
found.Subjects = viewerBinding.Subjects
log.Info("Updating role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
err = r.Update(context.TODO(), found)
if err != nil {
return err
}
}
return nil
}
// Create openpitrix runtime
func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) error {
if runtimeId := namespace.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" {
return nil
}
cm := &corev1.ConfigMap{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: constants.KubeSphereControlNamespace, Name: constants.AdminUserName}, cm)
if err != nil {
return err
}
runtime := &openpitrix.RunTime{Name: namespace.Name, Zone: namespace.Name, Provider: "kubernetes", RuntimeCredential: cm.Data["config"]}
log.Info("Creating openpitrix runtime", "namespace", namespace.Name)
if err := openpitrix.Client().CreateRuntime(runtime); err != nil {
return err
}
return nil
}
// Delete openpitrix runtime
func (r *ReconcileNamespace) deleteRuntime(namespace *corev1.Namespace) error {
if runtimeId := namespace.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" {
log.Info("Deleting openpitrix runtime", "namespace", namespace.Name, "runtime", runtimeId)
if err := openpitrix.Client().DeleteRuntime(runtimeId); err != nil {
return err
}
}
return nil
}
// Create openpitrix runtime
func (r *ReconcileNamespace) checkAndBindWorkspace(namespace *corev1.Namespace) error {
workspaceName := namespace.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return nil
}
workspace := &v1alpha1.Workspace{}
err := r.Get(context.TODO(), types.NamespacedName{Name: workspaceName}, workspace)
if err != nil {
if errors.IsNotFound(err) {
log.Error(err, "namespace", namespace.Name)
delete(namespace.Labels, constants.WorkspaceLabelKey)
err = r.Update(context.TODO(), namespace)
if err != nil {
return err
}
}
return err
}
if !metav1.IsControlledBy(namespace, workspace) {
if err := controllerutil.SetControllerReference(workspace, namespace, r.scheme); err != nil {
return err
}
log.Info("Bind workspace", "namespace", namespace.Name, "workspace", workspaceName)
err = r.Update(context.TODO(), namespace)
if err != nil {
return err
}
}
return nil
}
//Create Ceph secret in the new namespace
func (r *ReconcileNamespace) checkAndCreateCephSecret(namespace *corev1.Namespace) error {
newNsName := namespace.Name
scList := &v1.StorageClassList{}
err := r.List(context.TODO(), &client.ListOptions{}, scList)
if err != nil {
return err
}
for _, sc := range scList.Items {
if sc.Provisioner == "kubernetes.io/rbd" {
log.Info("would create Ceph user secret in storage class %s at namespace %s", sc.GetName(), newNsName)
if secretName, ok := sc.Parameters["userSecretName"]; ok {
secret := &corev1.Secret{}
r.Get(context.TODO(), types.NamespacedName{Namespace: core.NamespaceSystem, Name: secretName}, secret)
if err != nil {
if errors.IsNotFound(err) {
log.Error(err, "cannot find secret in namespace %s, error: %s", core.NamespaceSystem, secretName)
continue
}
log.Error(err, fmt.Sprintf("failed to find secret in namespace %s", core.NamespaceSystem))
continue
}
glog.Infof("succeed to find secret %s in namespace %s", secret.GetName(), secret.GetNamespace())
newSecret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: secret.Kind,
APIVersion: secret.APIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: secret.GetName(),
Namespace: newNsName,
Labels: secret.GetLabels(),
Annotations: secret.GetAnnotations(),
DeletionGracePeriodSeconds: secret.GetDeletionGracePeriodSeconds(),
ClusterName: secret.GetClusterName(),
},
Data: secret.Data,
StringData: secret.StringData,
Type: secret.Type,
}
log.Info(fmt.Sprintf("creating secret %s in namespace %s...", newSecret.GetName(), newSecret.GetNamespace()))
err = r.Create(context.TODO(), newSecret)
if err != nil {
log.Error(err, fmt.Sprintf("failed to create secret in namespace %s", newSecret.GetNamespace()))
continue
}
} else {
log.Error(err, fmt.Sprintf("failed to find user secret name in storage class %s", sc.GetName()))
}
}
}
return nil
}

View File

@@ -0,0 +1,77 @@
/*
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 namespace
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -0,0 +1,19 @@
/*
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 namespace

View File

@@ -0,0 +1,534 @@
/*
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 workspace
import (
"context"
"fmt"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
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"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller")
/**
* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
* business logic. Delete these comments after modifying this file.*
*/
// Add creates a new Workspace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileWorkspace{Client: mgr.GetClient(), scheme: mgr.GetScheme(),
recorder: mgr.GetRecorder("workspace-controller"), ksclient: kubesphere.Client()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("workspace-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Workspace
err = c.Watch(&source.Kind{Type: &tenantv1alpha1.Workspace{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileWorkspace{}
// ReconcileWorkspace reconciles a Workspace object
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
// and what is in the Workspace.Spec
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces/status,verbs=get;update;patch
func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Workspace instance
instance := &tenantv1alpha1.Workspace{}
err := r.Get(context.TODO(), request.NamespacedName, instance)
if err != nil {
if errors.IsNotFound(err) {
// Object not found, return. Created objects are automatically garbage collected.
// For additional cleanup logic use finalizers.
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// name of your custom finalizer
finalizer := "finalizers.tenant.kubesphere.io"
if instance.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object.
if !sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, finalizer)
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
} else {
// 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.deleteGroup(instance); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
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 {
return item == finalizer
})
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}
}
// Our finalizer has finished, so the reconciler can do nothing.
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.createGroup(instance); err != nil {
return reconcile.Result{}, err
}
if err = r.createWorkspaceRoleBindings(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) {
found.Rules = admin.Rules
found.Labels = admin.Labels
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) {
found.Rules = regular.Rules
found.Labels = regular.Labels
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) {
found.Rules = viewer.Rules
found.Labels = viewer.Labels
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) 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: getWorkspaceViewerRoleName(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 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, constants.DisplayNameLabelKey: constants.WorkspaceAdmin}
admin.Rules = []rbac.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"*"},
ResourceNames: []string{workspaceName},
Resources: []string{"workspaces", "workspaces/*"},
},
{
Verbs: []string{"list"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
}
return admin
}
func getWorkspaceRegular(workspaceName string) *rbac.ClusterRole {
regular := &rbac.ClusterRole{}
regular.Name = getWorkspaceRegularRoleName(workspaceName)
regular.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName, constants.DisplayNameLabelKey: constants.WorkspaceRegular}
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"},
},
}
return regular
}
func getWorkspaceViewer(workspaceName string) *rbac.ClusterRole {
viewer := &rbac.ClusterRole{}
viewer.Name = getWorkspaceViewerRoleName(workspaceName)
viewer.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName, constants.DisplayNameLabelKey: constants.WorkspaceViewer}
viewer.Rules = []rbac.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"*"},
ResourceNames: []string{workspaceName},
Resources: []string{"workspaces", "workspaces/*"},
},
}
return viewer
}

View File

@@ -0,0 +1,77 @@
/*
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 workspace
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -0,0 +1,19 @@
/*
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 workspace

View File

@@ -36,6 +36,10 @@ func Wrap(err error) Error {
return Error{Message: err.Error()}
}
func New(message string) Error {
return Error{Message: message}
}
func Parse(data []byte) error {
var j map[string]string
err := json.Unmarshal(data, &j)

View File

@@ -24,6 +24,7 @@ import (
s2iInformers "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions"
"k8s.io/client-go/informers"
ksInformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
)
@@ -33,8 +34,10 @@ const defaultResync = 600 * time.Second
var (
k8sOnce sync.Once
s2iOnce sync.Once
ksOnce sync.Once
informerFactory informers.SharedInformerFactory
s2iInformerFactory s2iInformers.SharedInformerFactory
ksInformerFactory ksInformers.SharedInformerFactory
)
func SharedInformerFactory() informers.SharedInformerFactory {
@@ -52,3 +55,11 @@ func S2iSharedInformerFactory() s2iInformers.SharedInformerFactory {
})
return s2iInformerFactory
}
func KsSharedInformerFactory() ksInformers.SharedInformerFactory {
ksOnce.Do(func() {
k8sClient := k8s.KsClient()
ksInformerFactory = ksInformers.NewSharedInformerFactory(k8sClient, defaultResync)
})
return ksInformerFactory
}

View File

@@ -18,11 +18,8 @@
package applications
import (
"encoding/json"
"fmt"
"github.com/golang/glog"
"io/ioutil"
v12 "k8s.io/api/apps/v1"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
@@ -33,24 +30,11 @@ import (
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"net/http"
"strconv"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"strings"
"time"
)
var (
OpenPitrixProxyToken string
OpenPitrixServer string
)
const (
unknown = "-"
deploySuffix = "-Deployment"
daemonSuffix = "-DaemonSet"
stateSuffix = "-StatefulSet"
)
type Application struct {
Name string `json:"name"`
RepoName string `json:"repoName"`
@@ -70,174 +54,93 @@ type Application struct {
ClusterID string `json:"cluster_id"`
}
type clusterRole struct {
ClusterID string `json:"cluster_id"`
Role string `json:"role"`
}
type cluster struct {
ClusterID string `json:"cluster_id"`
Name string `json:"name"`
AppID string `json:"app_id"`
VersionID string `json:"version_id"`
Status string `json:"status"`
UpdateTime time.Time `json:"status_time"`
CreateTime time.Time `json:"create_time"`
RunTimeId string `json:"runtime_id"`
Description string `json:"description"`
ClusterRoleSets []clusterRole `json:"cluster_role_set"`
}
type clusters struct {
Total int `json:"total_count"`
Clusters []cluster `json:"cluster_set"`
}
type versionList struct {
Total int `json:"total_count"`
Versions []version `json:"app_version_set"`
}
type version struct {
Name string `json:"name"`
VersionID string `json:"version_id"`
}
type runtime struct {
RuntimeID string `json:"runtime_id"`
Zone string `json:"zone"`
}
type runtimeList struct {
Total int `json:"total_count"`
Runtimes []runtime `json:"runtime_set"`
}
type app struct {
AppId string `json:"app_id"`
Name string `json:"name"`
ChartName string `json:"chart_name"`
RepoId string `json:"repo_id"`
}
type repo struct {
RepoId string `json:"repo_id"`
Name string `json:"name"`
Url string `json:"url"`
}
type workLoads struct {
Deployments []v12.Deployment `json:"deployments,omitempty"`
Statefulsets []v12.StatefulSet `json:"statefulsets,omitempty"`
Daemonsets []v12.DaemonSet `json:"daemonsets,omitempty"`
Deployments []appsv1.Deployment `json:"deployments,omitempty"`
Statefulsets []appsv1.StatefulSet `json:"statefulsets,omitempty"`
Daemonsets []appsv1.DaemonSet `json:"daemonsets,omitempty"`
}
type appList struct {
Total int `json:"total_count"`
Apps []app `json:"app_set"`
func ListApplication(runtimeId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) {
clusterList, err := openpitrix.ListClusters(runtimeId, conditions.Match["keyword"], conditions.Match["status"], limit, offset)
if err != nil {
return nil, err
}
result := models.PageableResponse{TotalCount: clusterList.Total}
result.Items = make([]interface{}, 0)
for _, item := range clusterList.Clusters {
var app Application
app.Name = item.Name
app.ClusterID = item.ClusterID
app.UpdateTime = item.UpdateTime
app.Status = item.Status
versionInfo, _ := openpitrix.GetVersion(item.VersionID)
app.Version = versionInfo
app.VersionId = item.VersionID
runtimeInfo, _ := openpitrix.GetRuntime(item.RunTimeId)
app.Runtime = runtimeInfo
app.RuntimeId = item.RunTimeId
appInfo, _, appId, _ := openpitrix.GetAppInfo(item.AppID)
app.App = appInfo
app.AppId = appId
app.Description = item.Description
result.Items = append(result.Items, app)
}
return &result, nil
}
type repoList struct {
Total int `json:"total_count"`
Repos []repo `json:"repo_set"`
func GetApp(clusterId string) (*Application, error) {
item, err := openpitrix.GetCluster(clusterId)
if err != nil {
return nil, err
}
var app Application
app.Name = item.Name
app.ClusterID = item.ClusterID
app.UpdateTime = item.UpdateTime
app.CreateTime = item.CreateTime
app.Status = item.Status
versionInfo, _ := openpitrix.GetVersion(item.VersionID)
app.Version = versionInfo
app.VersionId = item.VersionID
runtimeInfo, _ := openpitrix.GetRuntime(item.RunTimeId)
app.Runtime = runtimeInfo
app.RuntimeId = item.RunTimeId
appInfo, repoId, appId, _ := openpitrix.GetAppInfo(item.AppID)
app.App = appInfo
app.AppId = appId
app.Description = item.Description
app.RepoName, _ = openpitrix.GetRepo(repoId)
workloads, err := getWorkLoads(app.Runtime, item.ClusterRoleSets)
if err != nil {
glog.Error(err)
return nil, err
}
app.WorkLoads = workloads
workloadLabels := getLabels(app.Runtime, app.WorkLoads)
app.Services = getSvcs(app.Runtime, workloadLabels)
app.Ingresses = getIng(app.Runtime, app.Services)
return &app, nil
}
func GetAppInfo(appId string) (string, string, string, error) {
url := fmt.Sprintf("%s/v1/apps?app_id=%s", OpenPitrixServer, appId)
resp, err := makeHttpRequest("GET", url, "")
if err != nil {
glog.Error(err)
return unknown, unknown, unknown, err
}
var apps appList
err = json.Unmarshal(resp, &apps)
if err != nil {
glog.Error(err)
return unknown, unknown, unknown, err
}
if len(apps.Apps) == 0 {
return unknown, unknown, unknown, err
}
return apps.Apps[0].ChartName, apps.Apps[0].RepoId, apps.Apps[0].AppId, nil
}
func GetRepo(repoId string) (string, error) {
url := fmt.Sprintf("%s/v1/repos?repo_id=%s", OpenPitrixServer, repoId)
resp, err := makeHttpRequest("GET", url, "")
if err != nil {
glog.Error(err)
return unknown, err
}
var repos repoList
err = json.Unmarshal(resp, &repos)
if err != nil {
glog.Error(err)
return unknown, err
}
if len(repos.Repos) == 0 {
return unknown, err
}
return repos.Repos[0].Name, nil
}
func GetVersion(versionId string) (string, error) {
versionUrl := fmt.Sprintf("%s/v1/app_versions?version_id=%s", OpenPitrixServer, versionId)
resp, err := makeHttpRequest("GET", versionUrl, "")
if err != nil {
glog.Error(err)
return unknown, err
}
var versions versionList
err = json.Unmarshal(resp, &versions)
if err != nil {
glog.Error(err)
return unknown, err
}
if len(versions.Versions) == 0 {
return unknown, nil
}
return versions.Versions[0].Name, nil
}
func GetRuntime(runtimeId string) (string, error) {
versionUrl := fmt.Sprintf("%s/v1/runtimes?runtime_id=%s", OpenPitrixServer, runtimeId)
resp, err := makeHttpRequest("GET", versionUrl, "")
if err != nil {
glog.Error(err)
return unknown, err
}
var runtimes runtimeList
err = json.Unmarshal(resp, &runtimes)
if err != nil {
glog.Error(err)
return unknown, err
}
if len(runtimes.Runtimes) == 0 {
return unknown, nil
}
return runtimes.Runtimes[0].Zone, nil
}
func GetWorkLoads(namespace string, clusterRoles []clusterRole) (*workLoads, error) {
func getWorkLoads(namespace string, clusterRoles []openpitrix.ClusterRole) (*workLoads, error) {
var works workLoads
for _, clusterRole := range clusterRoles {
workLoadName := clusterRole.Role
if len(workLoadName) > 0 {
if strings.HasSuffix(workLoadName, deploySuffix) {
name := strings.Split(workLoadName, deploySuffix)[0]
if strings.HasSuffix(workLoadName, openpitrix.DeploySuffix) {
name := strings.Split(workLoadName, openpitrix.DeploySuffix)[0]
item, err := informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
@@ -249,8 +152,8 @@ func GetWorkLoads(namespace string, clusterRoles []clusterRole) (*workLoads, err
continue
}
if strings.HasSuffix(workLoadName, daemonSuffix) {
name := strings.Split(workLoadName, daemonSuffix)[0]
if strings.HasSuffix(workLoadName, openpitrix.DaemonSuffix) {
name := strings.Split(workLoadName, openpitrix.DaemonSuffix)[0]
item, err := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name)
if err != nil {
return nil, err
@@ -259,8 +162,8 @@ func GetWorkLoads(namespace string, clusterRoles []clusterRole) (*workLoads, err
continue
}
if strings.HasSuffix(workLoadName, stateSuffix) {
name := strings.Split(workLoadName, stateSuffix)[0]
if strings.HasSuffix(workLoadName, openpitrix.StateSuffix) {
name := strings.Split(workLoadName, openpitrix.StateSuffix)[0]
item, err := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name)
if err != nil {
return nil, err
@@ -346,7 +249,7 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
var ings []v1beta1.Ingress
for _, svc := range services {
result, err := resources.ListNamespaceResource(namespace, "ingress", &params.Conditions{Fuzzy: map[string]string{"serviceName": svc.Name}}, "", false, -1, 0)
result, err := resources.ListResources(namespace, "ingress", &params.Conditions{Fuzzy: map[string]string{"serviceName": svc.Name}}, "", false, -1, 0)
if err != nil {
glog.Error(err)
return nil
@@ -379,159 +282,3 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
return ings
}
func ListApplication(runtimeId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) {
if strings.HasSuffix(OpenPitrixServer, "/") {
OpenPitrixServer = strings.TrimSuffix(OpenPitrixServer, "/")
}
defaultStatus := "status=active&status=stopped&status=pending&status=ceased"
url := fmt.Sprintf("%s/v1/clusters?limit=%s&offset=%s", OpenPitrixServer, strconv.Itoa(limit), strconv.Itoa(offset))
if len(conditions.Fuzzy["name"]) > 0 {
url = fmt.Sprintf("%s&search_word=%s", url, conditions.Fuzzy["name"])
}
if len(conditions.Match["status"]) > 0 {
url = fmt.Sprintf("%s&status=%s", url, conditions.Match["status"])
} else {
url = fmt.Sprintf("%s&%s", url, defaultStatus)
}
if len(runtimeId) > 0 {
url = fmt.Sprintf("%s&runtime_id=%s", url, runtimeId)
}
resp, err := makeHttpRequest("GET", url, "")
if err != nil {
glog.Errorf("request %s failed, reason: %s", url, err)
return nil, err
}
var clusterList clusters
err = json.Unmarshal(resp, &clusterList)
if err != nil {
return nil, err
}
result := models.PageableResponse{TotalCount: clusterList.Total}
result.Items = make([]interface{}, 0)
for _, item := range clusterList.Clusters {
var app Application
app.Name = item.Name
app.ClusterID = item.ClusterID
app.UpdateTime = item.UpdateTime
app.Status = item.Status
versionInfo, _ := GetVersion(item.VersionID)
app.Version = versionInfo
app.VersionId = item.VersionID
runtimeInfo, _ := GetRuntime(item.RunTimeId)
app.Runtime = runtimeInfo
app.RuntimeId = item.RunTimeId
appInfo, _, appId, _ := GetAppInfo(item.AppID)
app.App = appInfo
app.AppId = appId
app.Description = item.Description
result.Items = append(result.Items, app)
}
return &result, nil
}
func GetApp(clusterId string) (*Application, error) {
if strings.HasSuffix(OpenPitrixServer, "/") {
OpenPitrixServer = strings.TrimSuffix(OpenPitrixServer, "/")
}
url := fmt.Sprintf("%s/v1/clusters?cluster_id=%s", OpenPitrixServer, clusterId)
resp, err := makeHttpRequest("GET", url, "")
if err != nil {
glog.Error(err)
return nil, err
}
var clusterList clusters
err = json.Unmarshal(resp, &clusterList)
if err != nil {
glog.Error(err)
return nil, err
}
if len(clusterList.Clusters) == 0 {
return nil, fmt.Errorf("NotFound, clusterId:%s", clusterId)
}
item := clusterList.Clusters[0]
var app Application
app.Name = item.Name
app.ClusterID = item.ClusterID
app.UpdateTime = item.UpdateTime
app.CreateTime = item.CreateTime
app.Status = item.Status
versionInfo, _ := GetVersion(item.VersionID)
app.Version = versionInfo
app.VersionId = item.VersionID
runtimeInfo, _ := GetRuntime(item.RunTimeId)
app.Runtime = runtimeInfo
app.RuntimeId = item.RunTimeId
appInfo, repoId, appId, _ := GetAppInfo(item.AppID)
app.App = appInfo
app.AppId = appId
app.Description = item.Description
app.RepoName, _ = GetRepo(repoId)
workloads, err := GetWorkLoads(app.Runtime, item.ClusterRoleSets)
if err != nil {
glog.Error(err)
return nil, err
}
app.WorkLoads = workloads
workloadLabels := getLabels(app.Runtime, app.WorkLoads)
app.Services = getSvcs(app.Runtime, workloadLabels)
app.Ingresses = getIng(app.Runtime, app.Services)
return &app, nil
}
func makeHttpRequest(method, url, data string) ([]byte, error) {
var req *http.Request
var err error
if method == "GET" {
req, err = http.NewRequest(method, url, nil)
} else {
req, err = http.NewRequest(method, url, strings.NewReader(data))
}
req.Header.Add("Authorization", OpenPitrixProxyToken)
if err != nil {
glog.Error(err)
return nil, err
}
httpClient := &http.Client{}
resp, err := httpClient.Do(req)
if err != nil {
err := fmt.Errorf("Request to %s failed, method: %s, reason: %s ", url, method, err)
glog.Error(err)
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
err = fmt.Errorf(string(body))
}
return body, err
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,188 +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 iam
import (
"fmt"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"k8s.io/api/rbac/v1"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/models"
)
const ClusterRoleKind = "ClusterRole"
// Get user list based on workspace role
func WorkspaceRoleUsers(workspace string, roleName string) ([]models.User, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
workspaceRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, roleName))
if err != nil {
return nil, err
}
names := make([]string, 0)
for _, subject := range workspaceRoleBinding.Subjects {
if subject.Kind == v1.UserKind {
names = append(names, subject.Name)
}
}
users, err := GetUsers(names)
if err != nil {
return nil, err
}
for i := 0; i < len(users); i++ {
users[i].WorkspaceRole = roleName
}
return users, nil
}
func GetUsers(names []string) ([]models.User, error) {
var users []models.User
if names == nil || len(names) == 0 {
return make([]models.User, 0), nil
}
conn, err := ldap.Client()
if err != nil {
return nil, err
}
for _, name := range names {
user, err := UserDetail(name, conn)
if err != nil {
return nil, err
}
users = append(users, *user)
}
return users, nil
}
func GetUser(name string) (*models.User, error) {
conn, err := ldap.Client()
if err != nil {
return nil, err
}
user, err := UserDetail(name, conn)
if err != nil {
return nil, err
}
return user, nil
}
func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespace bool, namespaces []string, err error) {
clusterRoles, err := GetClusterRoles(username)
if err != nil {
return false, nil, err
}
clusterRules := make([]v1.PolicyRule, 0)
for _, role := range clusterRoles {
clusterRules = append(clusterRules, role.Rules...)
}
if requiredRule.Size() == 0 {
if RulesMatchesRequired(clusterRules, v1.PolicyRule{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
}) {
return true, nil, nil
}
} else {
if RulesMatchesRequired(clusterRules, requiredRule) {
return true, nil, nil
}
}
roles, err := GetRoles("", username)
if err != nil {
return false, nil, err
}
rulesMapping := make(map[string][]v1.PolicyRule, 0)
for _, role := range roles {
rules := rulesMapping[role.Namespace]
if rules == nil {
rules = make([]v1.PolicyRule, 0)
}
rules = append(rules, role.Rules...)
rulesMapping[role.Namespace] = rules
}
namespaces = make([]string, 0)
for namespace, rules := range rulesMapping {
if requiredRule.Size() == 0 || RulesMatchesRequired(rules, requiredRule) {
namespaces = append(namespaces, namespace)
}
}
return false, namespaces, nil
}
func GetWorkspaceUsers(workspace string, workspaceRole string) ([]string, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, workspaceRole))
if err != nil {
return nil, err
}
users := make([]string, 0)
for _, s := range clusterRoleBinding.Subjects {
if s.Kind == v1.UserKind && !slice.ContainsString(users, s.Name, nil) {
users = append(users, s.Name)
}
}
return users, nil
}
func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}

View File

@@ -22,9 +22,12 @@ import (
"fmt"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/redis"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"regexp"
"sort"
"strconv"
"strings"
"time"
@@ -38,11 +41,10 @@ import (
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/models"
jwtutils "kubesphere.io/kubesphere/pkg/utils/jwt"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
)
var (
counter Counter
adminEmail string
adminPassword string
tokenExpireTime time.Duration
@@ -82,7 +84,7 @@ func checkAndCreateDefaultGroup(conn ldap.Client) error {
nil,
)
groups, err := conn.Search(groupSearchRequest)
_, err := conn.Search(groupSearchRequest)
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
err = createGroupsBaseDN(conn)
@@ -95,14 +97,6 @@ func checkAndCreateDefaultGroup(conn ldap.Client) error {
return fmt.Errorf("iam database init failed: %s\n", err)
}
if groups == nil || len(groups.Entries) == 0 {
_, err = CreateGroup(models.Group{Path: constants.SystemWorkspace, Name: constants.SystemWorkspace, Creator: constants.AdminUserName, Description: "system workspace"})
if err != nil {
return fmt.Errorf("system-workspace create failed: %s\n", err)
}
}
return nil
}
@@ -130,13 +124,10 @@ func checkAndCreateDefaultUser(conn ldap.Client) error {
}
if users == nil || len(users.Entries) == 0 {
counter = NewCounter(0)
err := CreateUser(models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default."})
_, err := CreateUser(&models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default."})
if err != nil {
return fmt.Errorf("admin create failed: %s\n", err)
}
} else {
counter = NewCounter(len(users.Entries))
}
return nil
@@ -164,12 +155,12 @@ func createGroupsBaseDN(conn ldap.Client) error {
}
// User login
func Login(username string, password string, ip string) (string, error) {
func Login(username string, password string, ip string) (*models.Token, error) {
conn, err := ldapclient.Client()
if err != nil {
return "", err
return nil, err
}
defer conn.Close()
@@ -185,11 +176,11 @@ func Login(username string, password string, ip string) (string, error) {
result, err := conn.Search(userSearchRequest)
if err != nil {
return "", err
return nil, err
}
if len(result.Entries) != 1 {
return "", ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password"))
return nil, ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password"))
}
uid := result.Entries[0].GetAttributeValue("uid")
@@ -200,7 +191,7 @@ func Login(username string, password string, ip string) (string, error) {
err = conn.Bind(dn, password)
if err != nil {
return "", err
return nil, err
}
claims := jwt.MapClaims{}
@@ -209,13 +200,11 @@ func Login(username string, password string, ip string) (string, error) {
claims["username"] = uid
claims["email"] = email
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
uToken, _ := token.SignedString(jwtutils.Secret)
token := jwtutil.MustSigned(claims)
loginLog(uid, ip)
return uToken, nil
return &models.Token{Token: token}, nil
}
func loginLog(uid, ip string) {
@@ -226,99 +215,6 @@ func loginLog(uid, ip string) {
}
}
func UserList(limit int, offset int) (int, []models.User, error) {
conn, err := ldapclient.Client()
if err != nil {
return 0, nil, err
}
defer conn.Close()
users := make([]models.User, 0)
pageControl := ldap.NewControlPaging(1000)
entries := make([]*ldap.Entry, 0)
cursor := 0
l1:
for {
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
"(&(objectClass=inetOrgPerson))",
[]string{"uid", "mail", "description"},
[]ldap.Control{pageControl},
)
response, err := conn.Search(userSearchRequest)
if err != nil {
return 0, nil, err
}
for _, entry := range response.Entries {
cursor++
if cursor > offset {
if len(entries) < limit {
entries = append(entries, entry)
} else {
break l1
}
}
}
updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging)
if ctrl, ok := updatedControl.(*ldap.ControlPaging); ctrl != nil && ok && len(ctrl.Cookie) != 0 {
pageControl.SetCookie(ctrl.Cookie)
continue
}
break
}
redisClient := redis.Client()
for _, v := range entries {
uid := v.GetAttributeValue("uid")
email := v.GetAttributeValue("mail")
description := v.GetAttributeValue("description")
user := models.User{Username: uid, Email: email, Description: description}
avatar, err := redisClient.HMGet("kubesphere:users:avatar", uid).Result()
if err != nil {
return 0, nil, err
}
if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok {
user.AvatarUrl = url
}
}
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -1, -1).Result()
if err != nil {
return 0, nil, err
}
if len(lastLogin) > 0 {
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
}
user.ClusterRules = make([]models.SimpleRule, 0)
users = append(users, user)
}
return counter.Get(), users, nil
}
func LoginLog(username string) ([]string, error) {
redisClient := redis.Client()
@@ -331,48 +227,77 @@ func LoginLog(username string) ([]string, error) {
return data, nil
}
func Search(keyword string, limit int, offset int) (int, []models.User, error) {
func ListUsersByName(names []string) (*models.PageableResponse, error) {
users := make([]*models.User, 0)
for _, name := range names {
if !k8sutil.ContainsUser(users, name) {
user, err := DescribeUser(name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
users = append(users, user)
}
}
items := make([]interface{}, 0)
for _, u := range users {
items = append(items, u)
}
return &models.PageableResponse{Items: items, TotalCount: len(items)}, nil
}
func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
conn, err := ldapclient.Client()
if err != nil {
return 0, nil, err
return nil, err
}
defer conn.Close()
users := make([]models.User, 0)
pageControl := ldap.NewControlPaging(80)
entries := make([]*ldap.Entry, 0)
users := make([]models.User, 0)
filter := "(&(objectClass=inetOrgPerson))"
if keyword := conditions.Match["keyword"]; keyword != "" {
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword)
}
cursor := 0
l1:
for {
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword),
[]string{"uid", "mail", "description"},
filter,
[]string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"},
[]ldap.Control{pageControl},
)
response, err := conn.Search(userSearchRequest)
if err != nil {
return 0, nil, err
return nil, err
}
for _, entry := range response.Entries {
cursor++
if cursor > offset {
if len(entries) < limit {
entries = append(entries, entry)
} else {
break l1
}
}
uid := entry.GetAttributeValue("uid")
email := entry.GetAttributeValue("mail")
description := entry.GetAttributeValue("description")
lang := entry.GetAttributeValue("preferredLanguage")
createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp"))
user := models.User{Username: uid, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
users = append(users, user)
}
updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging)
@@ -384,52 +309,104 @@ l1:
break
}
redisClient := redis.Client()
for _, v := range entries {
uid := v.GetAttributeValue("uid")
email := v.GetAttributeValue("mail")
description := v.GetAttributeValue("description")
user := models.User{Username: uid, Email: email, Description: description}
avatar, err := redisClient.HMGet("kubesphere:users:avatar", uid).Result()
if err != nil {
return 0, nil, err
sort.Slice(users, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
}
switch orderBy {
case "username":
fallthrough
case "createTime":
return users[i].CreateTime.Before(users[j].CreateTime)
default:
return strings.Compare(users[i].Username, users[j].Username) <= 0
}
})
if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok {
user.AvatarUrl = url
items := make([]interface{}, 0)
for i, user := range users {
if i >= offset && len(items) < limit {
avatar, err := getAvatar(user.Username)
if err != nil {
return nil, err
}
user.AvatarUrl = avatar
lastLoginTime, err := getLastLoginTime(user.Username)
if err != nil {
return nil, err
}
user.LastLoginTime = lastLoginTime
clusterRole, err := GetUserClusterRole(user.Username)
if err != nil {
return nil, err
}
user.ClusterRole = clusterRole.Name
items = append(items, user)
}
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -1, -1).Result()
if err != nil {
return 0, nil, err
}
if len(lastLogin) > 0 {
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
}
user.ClusterRules = make([]models.SimpleRule, 0)
users = append(users, user)
}
return counter.Get(), users, nil
return &models.PageableResponse{Items: items, TotalCount: len(users)}, nil
}
func UserDetail(username string, conn ldap.Client) (*models.User, error) {
func DescribeUser(username string) (*models.User, error) {
user, err := GetUserInfo(username)
if err != nil {
return nil, err
}
groups, err := GetUserGroups(username)
if err != nil {
return nil, err
}
user.Groups = groups
avatar, err := getAvatar(username)
if err != nil {
return nil, err
}
user.AvatarUrl = avatar
lastLoginTime, err := getLastLoginTime(username)
if err != nil {
return nil, err
}
user.LastLoginTime = lastLoginTime
return user, nil
}
// Get user info only included email description & lang
func GetUserInfo(username string) (*models.User, error) {
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=inetOrgPerson)(uid=%s))", username),
[]string{"mail", "description", "preferredLanguage"},
[]string{"mail", "description", "preferredLanguage", "createTimestamp"},
nil,
)
@@ -446,7 +423,20 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
email := result.Entries[0].GetAttributeValue("mail")
description := result.Entries[0].GetAttributeValue("description")
lang := result.Entries[0].GetAttributeValue("preferredLanguage")
user := models.User{Username: username, Email: email, Description: description, Lang: lang}
createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp"))
user := &models.User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
return user, nil
}
func GetUserGroups(username string) ([]string, error) {
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
defer conn.Close()
groupSearchRequest := ldap.NewSearchRequest(
ldapclient.GroupSearchBase,
@@ -456,11 +446,10 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
nil,
)
result, err = conn.Search(groupSearchRequest)
result, err := conn.Search(groupSearchRequest)
if err != nil {
return nil, err
}
groups := make([]string, 0)
@@ -470,41 +459,47 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
groups = append(groups, groupName)
}
user.Groups = groups
return groups, nil
}
redisClient := redis.Client()
avatar, err := redisClient.HMGet("kubesphere:users:avatar", username).Result()
func getLastLoginTime(username string) (string, error) {
lastLogin, err := redis.Client().LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
if err != nil {
return nil, err
return "", err
}
if len(lastLogin) > 0 {
return strings.Split(lastLogin[0], ",")[0], nil
}
return "", nil
}
func setAvatar(username, avatar string) error {
_, err := redis.Client().HMSet("kubesphere:users:avatar", map[string]interface{}{"username": avatar}).Result()
return err
}
func getAvatar(username string) (string, error) {
avatar, err := redis.Client().HMGet("kubesphere:users:avatar", username).Result()
if err != nil {
return "", err
}
if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok {
user.AvatarUrl = url
return url, nil
}
}
user.Status = 0
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
if err != nil {
return nil, err
}
if len(lastLogin) > 0 {
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
}
return &user, nil
return "", nil
}
func DeleteUser(username string) error {
// bind root DN
conn, err := ldapclient.Client()
if err != nil {
return err
}
@@ -521,13 +516,7 @@ func DeleteUser(username string) error {
err = deleteRoleBindings(username)
if err != nil {
return err
}
counter.Sub(1)
return nil
return err
}
func deleteRoleBindings(username string) error {
@@ -539,7 +528,7 @@ func deleteRoleBindings(username string) error {
}
for _, roleBinding := range roleBindings {
roleBinding = roleBinding.DeepCopy()
length1 := len(roleBinding.Subjects)
for index, subject := range roleBinding.Subjects {
@@ -571,6 +560,7 @@ func deleteRoleBindings(username string) error {
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
for _, clusterRoleBinding := range clusterRoleBindings {
clusterRoleBinding = clusterRoleBinding.DeepCopy()
length1 := len(clusterRoleBinding.Subjects)
for index, subject := range clusterRoleBinding.Subjects {
@@ -637,7 +627,7 @@ func UserCreateCheck(check string) (exist bool, err error) {
}
}
func CreateUser(user models.User) error {
func CreateUser(user *models.User) (*models.User, error) {
user.Username = strings.TrimSpace(user.Username)
user.Email = strings.TrimSpace(user.Email)
user.Password = strings.TrimSpace(user.Password)
@@ -646,7 +636,7 @@ func CreateUser(user models.User) error {
conn, err := ldapclient.Client()
if err != nil {
return err
return nil, err
}
defer conn.Close()
@@ -662,17 +652,17 @@ func CreateUser(user models.User) error {
result, err := conn.Search(userSearchRequest)
if err != nil {
return err
return nil, err
}
if len(result.Entries) > 0 {
return ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists"))
return nil, ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists"))
}
maxUid, err := getMaxUid(conn)
if err != nil {
return err
return nil, err
}
maxUid += 1
@@ -688,7 +678,7 @@ func CreateUser(user models.User) error {
userCreateRequest.Attribute("mail", []string{user.Email}) // RFC1274: RFC822 Mailbox
userCreateRequest.Attribute("userPassword", []string{user.Password}) // RFC4519/2307: password of user
if user.Lang != "" {
userCreateRequest.Attribute("preferredLanguage", []string{user.Lang}) // RFC4519/2307: password of user
userCreateRequest.Attribute("preferredLanguage", []string{user.Lang})
}
if user.Description != "" {
userCreateRequest.Attribute("description", []string{user.Description}) // RFC4519: descriptive information
@@ -697,16 +687,22 @@ func CreateUser(user models.User) error {
err = conn.Add(userCreateRequest)
if err != nil {
return err
return nil, err
}
counter.Add(1)
if user.AvatarUrl != "" {
setAvatar(user.Username, user.AvatarUrl)
}
if user.ClusterRole != "" {
CreateClusterRoleBinding(user.Username, user.ClusterRole)
err := CreateClusterRoleBinding(user.Username, user.ClusterRole)
if err != nil {
return nil, err
}
}
return nil
return DescribeUser(user.Username)
}
func getMaxUid(conn ldap.Client) (int, error) {
@@ -768,11 +764,12 @@ func getMaxGid(conn ldap.Client) (int, error) {
return maxGid, nil
}
func UpdateUser(user models.User) error {
func UpdateUser(user *models.User) (*models.User, error) {
conn, err := ldapclient.Client()
if err != nil {
return err
return nil, err
}
defer conn.Close()
@@ -794,19 +791,27 @@ func UpdateUser(user models.User) error {
userModifyRequest.Replace("userPassword", []string{user.Password})
}
if user.AvatarUrl != "" {
err = setAvatar(user.Username, user.AvatarUrl)
}
if err != nil {
return nil, err
}
err = conn.Modify(userModifyRequest)
if err != nil {
return err
return nil, err
}
err = CreateClusterRoleBinding(user.Username, user.ClusterRole)
if err != nil {
return err
return nil, err
}
return nil
return DescribeUser(user.Username)
}
func DeleteGroup(path string) error {
@@ -829,13 +834,14 @@ func DeleteGroup(path string) error {
return nil
}
func CreateGroup(group models.Group) (*models.Group, error) {
func CreateGroup(group *models.Group) (*models.Group, error) {
// bind root DN
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
defer conn.Close()
maxGid, err := getMaxGid(conn)
@@ -861,7 +867,9 @@ func CreateGroup(group models.Group) (*models.Group, error) {
groupCreateRequest.Attribute("description", []string{group.Description})
}
groupCreateRequest.Attribute("memberUid", []string{group.Creator})
if group.Members != nil {
groupCreateRequest.Attribute("memberUid", group.Members)
}
err = conn.Add(groupCreateRequest)
@@ -871,18 +879,7 @@ func CreateGroup(group models.Group) (*models.Group, error) {
group.Gid = strconv.Itoa(maxGid)
group.CreateTime = time.Now().UTC().Format("2006-01-02T15:04:05Z")
redisClient := redis.Client()
if err := redisClient.HMSet("kubesphere:groups:create-time", map[string]interface{}{group.Name: group.CreateTime}).Err(); err != nil {
return nil, err
}
if err := redisClient.HMSet("kubesphere:groups:creator", map[string]interface{}{group.Name: group.Creator}).Err(); err != nil {
return nil, err
}
return &group, nil
return DescribeGroup(group.Path)
}
func UpdateGroup(group *models.Group) (*models.Group, error) {
@@ -894,7 +891,7 @@ func UpdateGroup(group *models.Group) (*models.Group, error) {
}
defer conn.Close()
old, err := GroupDetail(group.Path, conn)
old, err := DescribeGroup(group.Path)
if err != nil {
return nil, err
@@ -1027,34 +1024,22 @@ func ChildList(path string) ([]models.Group, error) {
group.ChildGroups = childGroups
redisClient := redis.Client()
createTime, _ := redisClient.HMGet("kubesphere:groups:create-time", group.Name).Result()
if len(createTime) > 0 {
if t, ok := createTime[0].(string); ok {
group.CreateTime = t
}
}
creator, _ := redisClient.HMGet("kubesphere:groups:creator", group.Name).Result()
if len(creator) > 0 {
if t, ok := creator[0].(string); ok {
group.Creator = t
}
}
groups = append(groups, group)
}
return groups, nil
}
func GroupDetail(path string, conn ldap.Client) (*models.Group, error) {
func DescribeGroup(path string) (*models.Group, error) {
searchBase, cn := splitPath(path)
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
groupSearchRequest := ldap.NewSearchRequest(searchBase,
ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=posixGroup)(cn=%s))", cn),
@@ -1083,24 +1068,76 @@ func GroupDetail(path string, conn ldap.Client) (*models.Group, error) {
group.ChildGroups = childGroups
redisClient := redis.Client()
createTime, _ := redisClient.HMGet("kubesphere:groups:create-time", group.Name).Result()
if len(createTime) > 0 {
if t, ok := createTime[0].(string); ok {
group.CreateTime = t
}
}
creator, _ := redisClient.HMGet("kubesphere:groups:creator", group.Name).Result()
if len(creator) > 0 {
if t, ok := creator[0].(string); ok {
group.Creator = t
}
}
return &group, nil
}
func WorkspaceUsersTotalCount(workspace string) (int, error) {
workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace)
if err != nil {
return 0, err
}
users := make([]string, 0)
for _, roleBinding := range workspaceRoleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
users = append(users, subject.Name)
}
}
}
return len(users), nil
}
func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace)
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
for _, roleBinding := range workspaceRoleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
if err != nil {
return nil, err
}
prefix := fmt.Sprintf("workspace:%s:", workspace)
user.WorkspaceRole = fmt.Sprintf("workspace-%s", strings.TrimPrefix(roleBinding.Name, prefix))
users = append(users, user)
}
}
}
// order & reverse
sort.Slice(users, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
}
switch orderBy {
default:
fallthrough
case "name":
return strings.Compare(users[i].Username, users[j].Username) <= 0
}
})
result := make([]interface{}, 0)
for i, d := range users {
if i >= offset && (limit == -1 || len(result) < limit) {
result = append(result, d)
}
}
return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil
}

View File

@@ -20,6 +20,7 @@ package policy
import (
"encoding/json"
"fmt"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/models"
@@ -55,292 +56,25 @@ func init() {
}
var (
WorkspaceRoleRuleMapping = []models.Rule{
{
Name: "workspaces",
Actions: []models.Action{
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces"},
}, {
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/*"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"jenkins.kubesphere.io"},
Resources: []string{"*"},
}, {
Verbs: []string{"*"},
APIGroups: []string{"devops.kubesphere.io"},
Resources: []string{"*"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces"},
},
},
},
},
},
{Name: "members",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"patch", "update"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
},
},
{
Name: "devops",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
},
},
{
Name: "projects",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
},
},
{
Name: "organizations",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
}},
},
{
Name: "roles",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/roles"},
},
}},
},
},
}
ClusterRoleRuleMapping = []models.Rule{
{Name: "workspaces",
Actions: []models.Action{
{
Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"users"},
},
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"workspaces"},
Resources: []string{"monitoring/*"},
},
{
Verbs: []string{"list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"quota", "status", "monitoring", "persistentvolumeclaims"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"resources"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces", "workspaces/*"},
},
{
Verbs: []string{"get"},
APIGroups: []string{""},
Resources: []string{"namespaces"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"", "apps", "extensions", "batch"},
Resources: []string{"serviceaccounts", "limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumeclaims", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"rolebindings", "roles"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"members"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"router"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"jenkins.kubesphere.io", "devops.kubesphere.io"},
Resources: []string{"*"},
},
},
},
{
Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"tenant.kubesphere.io"},
Resources: []string{"workspaces"},
},
},
},
{
Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"tenant.kubesphere.io"},
Resources: []string{"workspaces"},
},
},
@@ -349,7 +83,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"tenant.kubesphere.io", "monitoring.kubesphere.io"},
Resources: []string{"workspaces", "workspaces/*"},
},
{
@@ -359,7 +93,7 @@ var (
},
{
Verbs: []string{"*"},
APIGroups: []string{"", "apps", "extensions", "batch"},
APIGroups: []string{"", "apps", "extensions", "batch", "resources.kubesphere.io"},
Resources: []string{"serviceaccounts", "limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumeclaims", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"},
},
{
@@ -367,16 +101,6 @@ var (
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"rolebindings", "roles"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"members"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"router"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"jenkins.kubesphere.io", "devops.kubesphere.io"},
@@ -391,9 +115,13 @@ var (
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"monitoring", "health", "monitoring/*"},
Verbs: []string{"get", "list"},
APIGroups: []string{"monitoring.kubesphere.io"},
Resources: []string{"*"},
}, {
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"health"},
}},
},
},
@@ -405,14 +133,14 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "watch", "list"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users", "users/*"},
},
{
Verbs: []string{"get"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"clusterrules"},
ResourceNames: []string{"mapping"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"rulesmapping"},
ResourceNames: []string{"clusterroles"},
},
{
Verbs: []string{"get", "watch", "list"},
@@ -425,12 +153,12 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"create", "get", "list"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
{
Verbs: []string{"get"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"clusterrules"},
ResourceNames: []string{"mapping"},
},
@@ -445,7 +173,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list", "update", "patch"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
{
@@ -459,8 +187,8 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete", "deletecollection"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"accounts"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
},
},
@@ -483,8 +211,8 @@ var (
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"clusterroles/*"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"clusterroles", "clusterroles/*"},
},
},
},
@@ -527,15 +255,9 @@ var (
APIGroups: []string{"storage.k8s.io"},
Resources: []string{"storageclasses"},
}, {
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"storage-classes"},
Resources: []string{"resources"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"storage/*"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"storageclasses", "storageclasses/*"},
},
},
},
@@ -578,15 +300,13 @@ var (
Resources: []string{"nodes", "events"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"nodes"},
Resources: []string{"resources", "monitoring", "monitoring/*"},
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"nodes", "nodes/*"},
}, {
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"pods"},
Resources: []string{"resources"},
Verbs: []string{"get", "list"},
APIGroups: []string{"monitoring.kubesphere.io"},
Resources: []string{"nodes"},
},
},
},
@@ -669,14 +389,9 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"list", "get"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"components", "components/*"},
},
{
Verbs: []string{"list", "get"},
APIGroups: []string{""},
Resources: []string{"pods"},
},
},
},
},
@@ -726,12 +441,12 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"rbac.authorization.k8s.io"},
APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"},
Resources: []string{"rolebindings"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
},
@@ -772,15 +487,9 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"rbac.authorization.k8s.io"},
APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"},
Resources: []string{"roles"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"roles"},
Resources: []string{"resources"},
},
},
},
{Name: "create",
@@ -819,7 +528,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"apps", "extensions"},
APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"},
Resources: []string{"deployments", "deployments/scale"},
},
{
@@ -875,7 +584,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"apps"},
APIGroups: []string{"apps", "resources.kubesphere.io"},
Resources: []string{"statefulsets"},
},
{
@@ -929,7 +638,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"apps", "extensions"},
APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"},
Resources: []string{"daemonsets"},
},
{
@@ -974,8 +683,17 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"pod/shell"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"pod/terminal"},
},
},
},
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"pods"},
},
},
},
@@ -997,7 +715,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"list", "get"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"services"},
},
},
@@ -1039,7 +757,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1048,7 +766,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1057,7 +775,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1066,7 +784,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1081,7 +799,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"extensions"},
APIGroups: []string{"extensions", "resources.kubesphere.io"},
Resources: []string{"ingresses"},
},
},
@@ -1121,7 +839,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"persistentvolumeclaims"},
},
},
@@ -1160,10 +878,9 @@ var (
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"applications"},
Resources: []string{"resources"},
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"applications"},
},
{
Verbs: []string{"list"},
@@ -1203,7 +920,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{"batch"},
APIGroups: []string{"batch", "resources.kubesphere.io"},
Resources: []string{"jobs"},
},
}},
@@ -1236,7 +953,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{"batch"},
APIGroups: []string{"batch", "resources.kubesphere.io"},
Resources: []string{"cronjobs"},
},
}},
@@ -1269,7 +986,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"secrets"},
},
}},
@@ -1302,7 +1019,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"configmaps"},
},
}},
@@ -1331,3 +1048,16 @@ var (
},
}
)
func GetClusterAction(module, action string) (models.Action, error) {
for _, rule := range ClusterRoleRuleMapping {
if rule.Name == module {
for _, act := range rule.Actions {
if act.Name == action {
return act, nil
}
}
}
}
return models.Action{}, fmt.Errorf("not found")
}

View File

@@ -27,4 +27,4 @@ const (
QueryLevelWorkload
QueryLevelPod
QueryLevelContainer
)
)

View File

@@ -248,7 +248,7 @@ func FluentbitOutputInsert(output fb.OutputPlugin) *FluentbitOutputsResult {
// 1. Update ConfigMap
var outputs []fb.OutputPlugin
outputs, err := GetFluentbitOutputFromConfigMap()
if err != nil {
if err != nil {
// If the ConfigMap doesn't exist, a new one will be created later
glog.Errorln(err)
}

View File

@@ -1,18 +1,18 @@
/*
Copyright 2019 The KubeSphere Authors.
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
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
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.
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.
*/
@@ -802,12 +802,12 @@ func MonitorOneWorkspaceStatistics(wsName string) *FormatedLevelMetric {
}()
go func() {
members, errMemb := workspaces.GetOrgMembers(wsName)
count, errMemb := workspaces.WorkspaceUserCount(wsName)
if errMemb != nil {
glog.Errorln(errMemb.Error())
}
// add member metric
memberMetrics = getSpecificMetricItem(timestamp, MetricNameWorkspaceMemberCount, WorkspaceResourceKindMember, len(members), errMemb)
memberMetrics = getSpecificMetricItem(timestamp, MetricNameWorkspaceMemberCount, WorkspaceResourceKindMember, count, errMemb)
wg.Done()
}()

View File

@@ -1,17 +1,19 @@
/*
Copyright 2018 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
Copyright 2019 The KubeSphere Authors.
http://www.apache.org/licenses/LICENSE-2.0
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.
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 metrics
@@ -255,6 +257,7 @@ func ReformatJson(metric string, metricsName string, needAddParams map[string]st
var formatMetric FormatedMetric
err := jsonIter.Unmarshal([]byte(metric), &formatMetric)
if err != nil {
glog.Errorln("Unmarshal metric json failed", err.Error(), metric)
}

View File

@@ -55,9 +55,9 @@ func getUsage(namespace, resource string) (int, error) {
var result *models.PageableResponse
var err error
if resource == resources.Namespaces || resource == resources.StorageClasses {
result, err = resources.ListClusterResource(resource, &params.Conditions{}, "", false, 1, 0)
result, err = resources.ListResources("", resource, &params.Conditions{}, "", false, 1, 0)
} else {
result, err = resources.ListNamespaceResource(namespace, resource, &params.Conditions{}, "", false, 1, 0)
result, err = resources.ListResources(namespace, resource, &params.Conditions{}, "", false, 1, 0)
}
if err != nil {

View File

@@ -20,6 +20,7 @@ package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"sort"
"strings"
@@ -30,16 +31,34 @@ import (
type clusterRoleSearcher struct {
}
func (*clusterRoleSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(name)
}
// exactly Match
func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRole) bool {
for k, v := range match {
switch k {
case ownerKind:
fallthrough
case ownerName:
kind := match[ownerKind]
name := match[ownerName]
if !k8sutil.IsControlledBy(item.OwnerReferences, kind, name) {
return false
}
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -62,10 +81,6 @@ func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRol
return false
}
return false
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -77,7 +92,7 @@ func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRol
func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough
@@ -86,7 +101,7 @@ func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool
}
}
func (s *clusterRoleSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
func (s *clusterRoleSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
clusterRoles, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().List(labels.Everything())
if err != nil {

View File

@@ -30,6 +30,10 @@ import (
type configMapSearcher struct {
}
func (*configMapSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name)
}
// exactly Match
func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool {
for k, v := range match {
@@ -38,8 +42,14 @@ func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) boo
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -66,10 +76,6 @@ func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) boo
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -81,7 +87,7 @@ func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) boo
func (*configMapSearcher) compare(a, b *v1.ConfigMap, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough

View File

@@ -31,6 +31,10 @@ import (
type cronJobSearcher struct {
}
func (*cronJobSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Batch().V1beta1().CronJobs().Lister().CronJobs(namespace).Get(name)
}
func cronJobStatus(item *v1beta1.CronJob) string {
if item.Spec.Suspend != nil && *item.Spec.Suspend {
return paused
@@ -42,12 +46,22 @@ func cronJobStatus(item *v1beta1.CronJob) string {
func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bool {
for k, v := range match {
switch k {
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
case status:
if cronJobStatus(item) != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -74,10 +88,6 @@ func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.CronJob) bo
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -98,7 +108,7 @@ func (*cronJobSearcher) compare(a, b *v1beta1.CronJob, orderBy string) bool {
return false
}
return a.Status.LastScheduleTime.Before(b.Status.LastScheduleTime)
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
default:
fallthrough

View File

@@ -30,6 +30,10 @@ import (
type daemonSetSearcher struct {
}
func (*daemonSetSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name)
}
func daemonSetStatus(item *v1.DaemonSet) string {
if item.Status.NumberAvailable == 0 {
return stopped
@@ -48,8 +52,18 @@ func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) boo
if daemonSetStatus(item) != v {
return false
}
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -76,10 +90,6 @@ func (*daemonSetSearcher) fuzzy(fuzzy map[string]string, item *v1.DaemonSet) boo
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -92,7 +102,7 @@ func (*daemonSetSearcher) fuzzy(fuzzy map[string]string, item *v1.DaemonSet) boo
func (*daemonSetSearcher) compare(a, b *v1.DaemonSet, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough

View File

@@ -31,6 +31,10 @@ import (
type deploymentSearcher struct {
}
func (*deploymentSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
}
func deploymentStatus(item *v1.Deployment) string {
if item.Spec.Replicas != nil {
if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 {
@@ -52,8 +56,18 @@ func (*deploymentSearcher) match(match map[string]string, item *v1.Deployment) b
if deploymentStatus(item) != v {
return false
}
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -80,10 +94,6 @@ func (*deploymentSearcher) fuzzy(fuzzy map[string]string, item *v1.Deployment) b
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -96,7 +106,7 @@ func (*deploymentSearcher) fuzzy(fuzzy map[string]string, item *v1.Deployment) b
func (*deploymentSearcher) compare(a, b *v1.Deployment, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough

View File

@@ -31,6 +31,10 @@ import (
type ingressSearcher struct {
}
func (*ingressSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name)
}
// exactly Match
func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress) bool {
for k, v := range match {
@@ -39,8 +43,14 @@ func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress)
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -67,10 +77,6 @@ func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *extensions.Ingress)
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -82,7 +88,7 @@ func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *extensions.Ingress)
func (*ingressSearcher) compare(a, b *extensions.Ingress, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough

View File

@@ -32,6 +32,10 @@ import (
type jobSearcher struct {
}
func (*jobSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Batch().V1().Jobs().Lister().Jobs(namespace).Get(name)
}
func jobStatus(item *batchv1.Job) string {
status := ""
@@ -54,8 +58,18 @@ func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool {
if jobStatus(item) != v {
return false
}
case name:
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -82,10 +96,6 @@ func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchv1.Job) bool {
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -111,6 +121,8 @@ func jobUpdateTime(item *batchv1.Job) time.Time {
func (*jobSearcher) compare(a, b *batchv1.Job, orderBy string) bool {
switch orderBy {
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case updateTime:
return jobUpdateTime(a).Before(jobUpdateTime(b))
case name:

View File

@@ -30,6 +30,10 @@ import (
type namespaceSearcher struct {
}
func (*namespaceSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(name)
}
// exactly Match
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
for k, v := range match {
@@ -38,8 +42,14 @@ func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) boo
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -66,10 +76,6 @@ func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) boo
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -81,7 +87,7 @@ func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) boo
func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough
@@ -90,7 +96,7 @@ func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
}
}
func (s *namespaceSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
func (s *namespaceSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
namespaces, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().List(labels.Everything())
if err != nil {

View File

@@ -30,6 +30,10 @@ import (
type nodeSearcher struct {
}
func (*nodeSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Core().V1().Nodes().Lister().Get(name)
}
// exactly Match
func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool {
for k, v := range match {
@@ -38,8 +42,14 @@ func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool {
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -66,10 +76,6 @@ func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool {
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -81,7 +87,7 @@ func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool {
func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough
@@ -90,7 +96,7 @@ func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool {
}
}
func (s *nodeSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
func (s *nodeSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
nodes, err := informers.SharedInformerFactory().Core().V1().Nodes().Lister().List(labels.Everything())
if err != nil {

View File

@@ -30,6 +30,10 @@ import (
type persistentVolumeClaimSearcher struct {
}
func (*persistentVolumeClaimSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).Get(name)
}
// exactly Match
func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool {
for k, v := range match {
@@ -38,8 +42,14 @@ func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.Pe
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -66,10 +76,6 @@ func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.Pe
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -81,7 +87,7 @@ func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.Pe
func (*persistentVolumeClaimSearcher) compare(a, b *v1.PersistentVolumeClaim, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough

View File

@@ -31,25 +31,29 @@ import (
type podSearcher struct {
}
func podBelongTo(item *v1.Pod, kind string, name string) bool {
func (*podSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Core().V1().Pods().Lister().Pods(namespace).Get(name)
}
if strings.EqualFold(kind, "Deployment") {
func podBelongTo(item *v1.Pod, kind string, name string) bool {
switch kind {
case "Deployment":
if podBelongToDeployment(item, name) {
return true
}
} else if strings.EqualFold(kind, "ReplicaSet") {
case "ReplicaSet":
if podBelongToReplicaSet(item, name) {
return true
}
} else if strings.EqualFold(kind, "DaemonSet") {
case "DaemonSet":
if podBelongToDaemonSet(item, name) {
return true
}
} else if strings.EqualFold(kind, "StatefulSet") {
case "StatefulSet":
if podBelongToStatefulSet(item, name) {
return true
}
} else if strings.EqualFold(kind, "Job") {
case "Job":
if podBelongToJob(item, name) {
return true
}
@@ -57,9 +61,9 @@ func podBelongTo(item *v1.Pod, kind string, name string) bool {
return false
}
func replicaSetBelongToDeployment(replicaSet *v12.ReplicaSet, name string) bool {
func replicaSetBelongToDeployment(replicaSet *v12.ReplicaSet, deploymentName string) bool {
for _, owner := range replicaSet.OwnerReferences {
if owner.Kind == "Deployment" && owner.Name == name {
if owner.Kind == "Deployment" && owner.Name == deploymentName {
return true
}
}
@@ -84,38 +88,36 @@ func podBelongToJob(item *v1.Pod, name string) bool {
return false
}
func podBelongToReplicaSet(item *v1.Pod, name string) bool {
func podBelongToReplicaSet(item *v1.Pod, replicaSetName string) bool {
for _, owner := range item.OwnerReferences {
if owner.Kind == "ReplicaSet" && owner.Name == name {
if owner.Kind == "ReplicaSet" && owner.Name == replicaSetName {
return true
}
}
return false
}
func podBelongToStatefulSet(item *v1.Pod, name string) bool {
replicas, err := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything())
if err != nil {
return false
}
for _, r := range replicas {
if replicaSetBelongToDeployment(r, name) {
return podBelongToReplicaSet(item, r.Name)
func podBelongToStatefulSet(item *v1.Pod, statefulSetName string) bool {
for _, owner := range item.OwnerReferences {
if owner.Kind == "StatefulSet" && owner.Name == statefulSetName {
return true
}
}
return false
}
func podBelongToDeployment(item *v1.Pod, name string) bool {
func podBelongToDeployment(item *v1.Pod, deploymentName string) bool {
replicas, err := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything())
if err != nil {
return false
}
for _, r := range replicas {
if replicaSetBelongToDeployment(r, name) {
return podBelongToReplicaSet(item, r.Name)
if replicaSetBelongToDeployment(r, deploymentName) && podBelongToReplicaSet(item, r.Name) {
return true
}
}
return false
}
@@ -134,10 +136,10 @@ func podBelongToService(item *v1.Pod, serviceName string) bool {
if err != nil {
return false
}
for k, v := range service.Spec.Selector {
if item.Labels[k] != v {
return false
}
selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated()
if !selector.Matches(labels.Set(item.Labels)) {
return false
}
return true
}
@@ -146,11 +148,11 @@ func podBelongToService(item *v1.Pod, serviceName string) bool {
func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
for k, v := range match {
switch k {
case "ownerKind":
case ownerKind:
fallthrough
case "ownerName":
kind := match["ownerKind"]
name := match["ownerName"]
case ownerName:
kind := match[ownerKind]
name := match[ownerName]
if !podBelongTo(item, kind, name) {
return false
}
@@ -170,6 +172,10 @@ func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if item.Labels[k] != v {
return false
@@ -200,10 +206,6 @@ func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool {
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -215,7 +217,7 @@ func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool {
func (*podSearcher) compare(a, b *v1.Pod, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough

View File

@@ -21,39 +21,45 @@ import (
"fmt"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"strings"
)
func init() {
namespacedResources[ConfigMaps] = &configMapSearcher{}
namespacedResources[CronJobs] = &cronJobSearcher{}
namespacedResources[DaemonSets] = &daemonSetSearcher{}
namespacedResources[Deployments] = &deploymentSearcher{}
namespacedResources[Ingresses] = &ingressSearcher{}
namespacedResources[Jobs] = &jobSearcher{}
namespacedResources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{}
namespacedResources[Secrets] = &secretSearcher{}
namespacedResources[Services] = &serviceSearcher{}
namespacedResources[StatefulSets] = &statefulSetSearcher{}
namespacedResources[Pods] = &podSearcher{}
namespacedResources[Roles] = &roleSearcher{}
namespacedResources[S2iBuilders] = &s2iBuilderSearcher{}
namespacedResources[S2iRuns] = &s2iRunSearcher{}
resources[ConfigMaps] = &configMapSearcher{}
resources[CronJobs] = &cronJobSearcher{}
resources[DaemonSets] = &daemonSetSearcher{}
resources[Deployments] = &deploymentSearcher{}
resources[Ingresses] = &ingressSearcher{}
resources[Jobs] = &jobSearcher{}
resources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{}
resources[Secrets] = &secretSearcher{}
resources[Services] = &serviceSearcher{}
resources[StatefulSets] = &statefulSetSearcher{}
resources[Pods] = &podSearcher{}
resources[Roles] = &roleSearcher{}
resources[S2iBuilders] = &s2iBuilderSearcher{}
resources[S2iRuns] = &s2iRunSearcher{}
clusterResources[Nodes] = &nodeSearcher{}
clusterResources[Namespaces] = &namespaceSearcher{}
clusterResources[ClusterRoles] = &clusterRoleSearcher{}
clusterResources[StorageClasses] = &storageClassesSearcher{}
clusterResources[S2iBuilderTemplates] = &s2iBuilderTemplateSearcher{}
resources[Nodes] = &nodeSearcher{}
resources[Namespaces] = &namespaceSearcher{}
resources[ClusterRoles] = &clusterRoleSearcher{}
resources[StorageClasses] = &storageClassesSearcher{}
resources[S2iBuilderTemplates] = &s2iBuilderTemplateSearcher{}
resources[Workspaces] = &workspaceSearcher{}
}
var namespacedResources = make(map[string]namespacedSearcherInterface)
var clusterResources = make(map[string]clusterSearcherInterface)
var (
resources = make(map[string]resourceSearchInterface)
clusterResources = []string{Nodes, Workspaces, Namespaces, ClusterRoles, StorageClasses, S2iBuilderTemplates}
)
const (
name = "name"
label = "label"
createTime = "createTime"
ownerKind = "ownerKind"
ownerName = "ownerName"
CreateTime = "CreateTime"
updateTime = "updateTime"
lastScheduleTime = "lastScheduleTime"
displayName = "displayName"
@@ -72,6 +78,8 @@ const (
Deployments = "deployments"
DaemonSets = "daemonsets"
Roles = "roles"
Workspaces = "workspaces"
WorkspaceRoles = "workspaceroles"
CronJobs = "cronjobs"
ConfigMaps = "configmaps"
Ingresses = "ingresses"
@@ -90,72 +98,58 @@ const (
S2iRuns = "s2iruns"
)
type namespacedSearcherInterface interface {
type resourceSearchInterface interface {
get(namespace, name string) (interface{}, error)
search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
}
type clusterSearcherInterface interface {
search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
func ListResourcesByName(namespace, resource string, names []string) (*models.PageableResponse, error) {
items := make([]interface{}, 0)
if searcher, ok := resources[resource]; ok {
for _, name := range names {
item, err := searcher.get(namespace, name)
if err != nil {
return nil, err
}
items = append(items, item)
}
} else {
return nil, fmt.Errorf("not found")
}
return &models.PageableResponse{TotalCount: len(items), Items: items}, nil
}
func ListNamespaceResource(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
func ListResources(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
items := make([]interface{}, 0)
total := 0
var err error
var result []interface{}
if searcher, ok := namespacedResources[resource]; ok {
// none namespace resource
if namespace != "" && sliceutil.HasString(clusterResources, resource) {
return nil, fmt.Errorf("not found")
}
if searcher, ok := resources[resource]; ok {
result, err = searcher.search(namespace, conditions, orderBy, reverse)
} else {
return nil, fmt.Errorf("not support")
return nil, fmt.Errorf("not found")
}
if err != nil {
return nil, err
}
total = len(result)
for i, d := range result {
if i >= offset && (limit == -1 || len(items) < limit) {
items = append(items, d)
}
}
return &models.PageableResponse{TotalCount: total, Items: items}, nil
}
func ListClusterResource(resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
items := make([]interface{}, 0)
total := 0
var err error
if err != nil {
return nil, err
}
var result []interface{}
if searcher, ok := clusterResources[resource]; ok {
result, err = searcher.search(conditions, orderBy, reverse)
} else if searcher, ok := namespacedResources[resource]; ok {
result, err = searcher.search("", conditions, orderBy, reverse)
} else {
return nil, fmt.Errorf("not support")
}
if err != nil {
return nil, err
}
total = len(result)
for i, d := range result {
if i >= offset && len(items) < limit {
items = append(items, d)
}
}
return &models.PageableResponse{TotalCount: total, Items: items}, nil
return &models.PageableResponse{TotalCount: len(result), Items: items}, nil
}
func searchFuzzy(m map[string]string, key, value string) bool {

View File

@@ -30,6 +30,10 @@ import (
type roleSearcher struct {
}
func (*roleSearcher) get(namespace, name string) (interface{}, error) {
return informers.SharedInformerFactory().Rbac().V1().Roles().Lister().Roles(namespace).Get(name)
}
// exactly Match
func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool {
for k, v := range match {
@@ -38,8 +42,14 @@ func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool {
if item.Name != v && item.Labels[displayName] != v {
return false
}
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
return false
if item.Labels[k] != v {
return false
}
}
}
return true
@@ -62,10 +72,6 @@ func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool {
return false
}
return false
case keyword:
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false
}
default:
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
return false
@@ -77,7 +83,7 @@ func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool {
func (*roleSearcher) compare(a, b *rbac.Role, orderBy string) bool {
switch orderBy {
case createTime:
case CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
case name:
fallthrough

Some files were not shown because too many files have changed in this diff Show More