Merge branch 'dev' into devops-refactor

This commit is contained in:
runzexia
2020-03-30 10:26:13 +08:00
committed by GitHub
408 changed files with 73577 additions and 4947 deletions

View File

@@ -26,7 +26,6 @@ import (
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
"kubesphere.io/kubesphere/pkg/server/params"
dsClient "kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
@@ -38,7 +37,6 @@ type DevOpsProjectOperator interface {
CreateDevOpsProject(username string, workspace string, req *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error)
GetDevOpsProjectsCount(username string) (uint32, error)
DeleteDevOpsProject(projectId, username string) error
GetUserDevOpsSimpleRules(username, projectId string) ([]policy.SimpleRule, error)
}
type devopsProjectOperator struct {
@@ -208,16 +206,6 @@ func (o *devopsProjectOperator) CreateDevOpsProject(username string, workspace s
return project, nil
}
func (o *devopsProjectOperator) GetUserDevOpsSimpleRules(username, projectId string) ([]policy.SimpleRule, error) {
role, err := o.getProjectUserRole(username, projectId)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusForbidden, err.Error())
}
return GetDevopsRoleSimpleRules(role), nil
}
func (o *devopsProjectOperator) getProjectUserRole(username, projectId string) (string, error) {
if username == devops.KS_ADMIN {
return dsClient.ProjectOwner, nil
@@ -235,47 +223,3 @@ func (o *devopsProjectOperator) getProjectUserRole(username, projectId string) (
return membership.Role, nil
}
func GetDevopsRoleSimpleRules(role string) []policy.SimpleRule {
var rules []policy.SimpleRule
switch role {
case "developer":
rules = []policy.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 = []policy.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 = []policy.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 = []policy.SimpleRule{
{Name: "pipelines", Actions: []string{"view"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
}
break
}
return rules
}

View File

@@ -19,17 +19,13 @@ package tenant
import (
"k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/labels"
k8sinformers "k8s.io/client-go/informers"
kubernetes "k8s.io/client-go/kubernetes"
"k8s.io/klog"
"k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"sort"
"strings"
)
@@ -41,7 +37,7 @@ type NamespaceInterface interface {
type namespaceSearcher struct {
k8s kubernetes.Interface
informers k8sinformers.SharedInformerFactory
am iam.AccessManagementInterface
am am.AccessManagementInterface
}
func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error) {
@@ -57,7 +53,7 @@ func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Name
return s.k8s.CoreV1().Namespaces().Create(namespace)
}
func newNamespaceOperator(k8s kubernetes.Interface, informers k8sinformers.SharedInformerFactory, am iam.AccessManagementInterface) NamespaceInterface {
func newNamespaceOperator(k8s kubernetes.Interface, informers k8sinformers.SharedInformerFactory, am am.AccessManagementInterface) NamespaceInterface {
return &namespaceSearcher{k8s: k8s, informers: informers, am: am}
}
@@ -111,76 +107,9 @@ func (s *namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
}
func (s *namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) {
roles, err := s.am.GetRoles("", username)
if err != nil {
return nil, err
}
namespaces := make([]*v1.Namespace, 0)
namespaceLister := s.informers.Core().V1().Namespaces().Lister()
for _, role := range roles {
namespace, err := namespaceLister.Get(role.Namespace)
if err != nil {
klog.Errorf("get namespace failed: %+v", err)
return nil, err
}
if !containsNamespace(namespaces, namespace) {
namespaces = append(namespaces, namespace)
}
}
return namespaces, nil
}
func containsNamespace(namespaces []*v1.Namespace, namespace *v1.Namespace) bool {
for _, item := range namespaces {
if item.Name == namespace.Name {
return true
}
}
return false
panic("implement me")
}
func (s *namespaceSearcher) Search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) {
rules, err := s.am.GetClusterPolicyRules(username)
if err != nil {
return nil, err
}
namespaces := make([]*v1.Namespace, 0)
if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"namespaces"}}) {
namespaces, err = s.informers.Core().V1().Namespaces().Lister().List(labels.Everything())
} else {
namespaces, err = s.GetNamespaces(username)
}
if err != nil {
return nil, err
}
result := make([]*v1.Namespace, 0)
for _, namespace := range namespaces {
if s.match(conditions.Match, namespace) && s.fuzzy(conditions.Fuzzy, namespace) {
result = append(result, namespace)
}
}
// order & reverse
sort.Slice(result, func(i, j int) bool {
if reverse {
i, j = j, i
}
return s.compare(result[i], result[j], orderBy)
})
return result, nil
}
func CreateNamespace() {
panic("implement me")
}

View File

@@ -19,20 +19,14 @@ package tenant
import (
"k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
k8sinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"strconv"
)
type Interface interface {
@@ -42,17 +36,14 @@ type Interface interface {
ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
ListDevopsProjects(username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
GetWorkspaceSimpleRules(workspace, username string) ([]policy.SimpleRule, error)
GetNamespaceSimpleRules(namespace, username string) ([]policy.SimpleRule, error)
CountDevOpsProjects(username string) (uint32, error)
DeleteDevOpsProject(username, projectId string) error
GetUserDevopsSimpleRules(username string, devops string) (interface{}, error)
}
type tenantOperator struct {
workspaces WorkspaceInterface
namespaces NamespaceInterface
am iam.AccessManagementInterface
am am.AccessManagementInterface
devops DevOpsProjectOperator
}
@@ -65,7 +56,7 @@ func (t *tenantOperator) DeleteDevOpsProject(username, projectId string) error {
}
func (t *tenantOperator) GetUserDevopsSimpleRules(username string, projectId string) (interface{}, error) {
return t.devops.GetUserDevOpsSimpleRules(username, projectId)
panic("implement me")
}
func (t *tenantOperator) ListDevopsProjects(username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
@@ -77,7 +68,7 @@ func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error {
}
func New(client kubernetes.Interface, informers k8sinformers.SharedInformerFactory, ksinformers ksinformers.SharedInformerFactory, db *mysql.Database) Interface {
amOperator := iam.NewAMOperator(client, informers)
amOperator := am.NewAMOperator(client, informers)
return &tenantOperator{
workspaces: newWorkspaceOperator(client, informers, ksinformers, amOperator, db),
namespaces: newNamespaceOperator(client, informers, amOperator),
@@ -96,105 +87,11 @@ func (t *tenantOperator) DescribeWorkspace(username, workspaceName string) (*v1a
return nil, err
}
if username != "" {
workspace = t.appendAnnotations(username, workspace)
}
return workspace, nil
}
func (t *tenantOperator) ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
workspaces, err := t.workspaces.SearchWorkspace(username, conditions, orderBy, reverse)
if err != nil {
return nil, err
}
// limit offset
result := make([]interface{}, 0)
for i, workspace := range workspaces {
if len(result) < limit && i >= offset {
workspace := t.appendAnnotations(username, workspace)
result = append(result, workspace)
}
}
return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil
}
func (t *tenantOperator) GetWorkspaceSimpleRules(workspace, username string) ([]policy.SimpleRule, error) {
clusterRules, err := t.am.GetClusterPolicyRules(username)
if err != nil {
return nil, err
}
// cluster-admin
if iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
}) {
return t.am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil
}
workspaceRole, err := t.am.GetWorkspaceRole(workspace, username)
// workspaces-manager
if iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"workspaces", "workspaces/*"},
}) {
return t.am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil
}
if err != nil {
if apierrors.IsNotFound(err) {
return []policy.SimpleRule{}, nil
}
klog.Error(err)
return nil, err
}
return t.am.GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil
}
func (t *tenantOperator) GetNamespaceSimpleRules(namespace, username string) ([]policy.SimpleRule, error) {
clusterRules, err := t.am.GetClusterPolicyRules(username)
if err != nil {
return nil, err
}
rules, err := t.am.GetPolicyRules(namespace, username)
if err != nil {
return nil, err
}
rules = append(rules, clusterRules...)
return iam.ConvertToSimpleRule(rules), nil
}
func (t *tenantOperator) appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace {
workspace = workspace.DeepCopy()
if workspace.Annotations == nil {
workspace.Annotations = make(map[string]string)
}
if ns, err := t.ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0); err == nil {
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
}
if devops, err := t.ListDevopsProjects(username, &params.Conditions{Match: map[string]string{"workspace": workspace.Name}}, "", false, 1, 0); err == nil {
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
}
userCount, err := t.workspaces.CountUsersInWorkspace(workspace.Name)
if err == nil {
workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount)
}
return workspace
panic("implement me")
}
func (t *tenantOperator) ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {

View File

@@ -18,27 +18,22 @@
package tenant
import (
"fmt"
core "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"sort"
"strings"
"k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@@ -69,14 +64,14 @@ type workspaceOperator struct {
client kubernetes.Interface
informers informers.SharedInformerFactory
ksInformers externalversions.SharedInformerFactory
am iam.AccessManagementInterface
am am.AccessManagementInterface
// TODO: use db interface instead of mysql client
// we can refactor this after rewrite devops using crd
db *mysql.Database
}
func newWorkspaceOperator(client kubernetes.Interface, informers informers.SharedInformerFactory, ksinformers externalversions.SharedInformerFactory, am iam.AccessManagementInterface, db *mysql.Database) WorkspaceInterface {
func newWorkspaceOperator(client kubernetes.Interface, informers informers.SharedInformerFactory, ksinformers externalversions.SharedInformerFactory, am am.AccessManagementInterface, db *mysql.Database) WorkspaceInterface {
return &workspaceOperator{
client: client,
informers: informers,
@@ -111,96 +106,12 @@ func (w *workspaceOperator) DeleteNamespace(workspace string, namespace string)
}
func (w *workspaceOperator) RemoveUser(workspace string, username string) error {
workspaceRole, err := w.am.GetWorkspaceRole(workspace, username)
if err != nil {
return err
}
err = w.deleteWorkspaceRoleBinding(workspace, username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey])
if err != nil {
return err
}
return nil
panic("implement me")
}
func (w *workspaceOperator) AddUser(workspaceName string, user *InWorkspaceUser) error {
workspaceRole, err := w.am.GetWorkspaceRole(workspaceName, user.Username)
if err != nil && !apierrors.IsNotFound(err) {
klog.Errorf("get workspace role failed: %+v", err)
return err
}
workspaceRoleName := fmt.Sprintf("workspace:%s:%s", workspaceName, strings.TrimPrefix(user.WorkspaceRole, "workspace-"))
var currentWorkspaceRoleName string
if workspaceRole != nil {
currentWorkspaceRoleName = workspaceRole.Name
}
if currentWorkspaceRoleName != workspaceRoleName && currentWorkspaceRoleName != "" {
err := w.deleteWorkspaceRoleBinding(workspaceName, user.Username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey])
if err != nil {
klog.Errorf("delete workspace role binding failed: %+v", err)
return err
}
} else if currentWorkspaceRoleName != "" {
return nil
}
return w.createWorkspaceRoleBinding(workspaceName, user.Username, user.WorkspaceRole)
}
func (w *workspaceOperator) createWorkspaceRoleBinding(workspace, username string, role string) error {
if !sliceutil.HasString(constants.WorkSpaceRoles, role) {
return apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role)
}
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
workspaceRoleBinding, err := w.informers.Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
if err != nil {
return err
}
if !iam.ContainsUser(workspaceRoleBinding.Subjects, username) {
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects, v1.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: username})
_, err = w.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding)
if err != nil {
klog.Errorf("update workspace role binding failed: %+v", err)
return err
}
}
return nil
}
func (w *workspaceOperator) deleteWorkspaceRoleBinding(workspace, username string, role string) error {
if !sliceutil.HasString(constants.WorkSpaceRoles, role) {
return apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role)
}
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
workspaceRoleBinding, err := w.informers.Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
if err != nil {
return err
}
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
for i, v := range workspaceRoleBinding.Subjects {
if v.Kind == v1.UserKind && v.Name == username {
workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects[:i], workspaceRoleBinding.Subjects[i+1:]...)
i--
}
}
workspaceRoleBinding, err = w.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding)
return err
panic("implement me")
}
func (w *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string) (int, error) {
@@ -288,50 +199,7 @@ func (*workspaceOperator) compare(a, b *v1alpha1.Workspace, orderBy string) bool
}
func (w *workspaceOperator) SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) {
rules, err := w.am.GetClusterPolicyRules(username)
if err != nil {
return nil, err
}
workspaces := make([]*v1alpha1.Workspace, 0)
if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"workspaces"}}) {
workspaces, err = w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything())
if err != nil {
return nil, err
}
} else {
workspaceRoles, err := w.am.GetWorkspaceRoleMap(username)
if err != nil {
return nil, err
}
for k := range workspaceRoles {
workspace, err := w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().Get(k)
if err != nil {
return nil, err
}
workspaces = append(workspaces, workspace)
}
}
result := make([]*v1alpha1.Workspace, 0)
for _, workspace := range workspaces {
if w.match(conditions.Match, workspace) && w.fuzzy(conditions.Fuzzy, workspace) {
result = append(result, workspace)
}
}
// order & reverse
sort.Slice(result, func(i, j int) bool {
if reverse {
i, j = j, i
}
return w.compare(result[i], result[j], orderBy)
})
return result, nil
panic("implement me")
}
func (w *workspaceOperator) GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {