[WIP] API refactor (#1737)

* refactor openpitrix API

Signed-off-by: hongming <talonwan@yunify.com>

* add openpitrix mock client

Signed-off-by: hongming <talonwan@yunify.com>

* refactor tenant API

Signed-off-by: hongming <talonwan@yunify.com>

* refactor IAM API

Signed-off-by: hongming <talonwan@yunify.com>

* refactor IAM API

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-01-13 13:36:21 +08:00
committed by zryfish
parent c40d1542a2
commit 71849f028f
66 changed files with 5415 additions and 4366 deletions

View File

@@ -25,14 +25,16 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apiserver/resources"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
"kubesphere.io/kubesphere/pkg/models/kubectl"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/clusterrole"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/role"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
@@ -47,12 +49,30 @@ const (
NamespaceViewerRoleBindName = "viewer"
)
func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
var rules []models.SimpleRule
type AccessManagementInterface interface {
GetDevopsRoleSimpleRules(role string) []SimpleRule
ListRoleBindings(namespace string, role string) ([]*rbacv1.RoleBinding, error)
CreateClusterRoleBinding(username string, clusterRole string) error
}
type amOperator struct {
informers informers.SharedInformerFactory
resources resource.ResourceGetter
}
func newAMOperator(informers informers.SharedInformerFactory) AccessManagementInterface {
resourceGetter := resource.ResourceGetter{}
resourceGetter.Add(v1alpha2.Role, role.NewRoleSearcher(informers))
resourceGetter.Add(v1alpha2.ClusterRoles, clusterrole.NewClusterRoleSearcher(informers))
return &amOperator{informers: informers, resources: resourceGetter}
}
func (am *amOperator) GetDevopsRoleSimpleRules(role string) []SimpleRule {
var rules []SimpleRule
switch role {
case "developer":
rules = []models.SimpleRule{
rules = []SimpleRule{
{Name: "pipelines", Actions: []string{"view", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
@@ -60,7 +80,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
}
break
case "owner":
rules = []models.SimpleRule{
rules = []SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"create", "edit", "view", "delete"}},
@@ -69,7 +89,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
}
break
case "maintainer":
rules = []models.SimpleRule{
rules = []SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
@@ -80,7 +100,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
case "reporter":
fallthrough
default:
rules = []models.SimpleRule{
rules = []SimpleRule{
{Name: "pipelines", Actions: []string{"view"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
@@ -92,14 +112,14 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
}
// Get user roles in namespace
func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
func (am *amOperator) GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) {
clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister()
roleBindingLister := am.informers.Rbac().V1().RoleBindings().Lister()
roleLister := am.informers.Rbac().V1().Roles().Lister()
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
klog.Errorln("get role bindings", namespace, err)
klog.Errorln(err)
return nil, err
}
@@ -114,7 +134,7 @@ func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) {
klog.Warningf("cluster role %s not found but bind user %s in namespace %s", roleBinding.RoleRef.Name, username, namespace)
continue
} else {
klog.Errorln("get cluster role", roleBinding.RoleRef.Name, err)
klog.Errorln(err)
return nil, err
}
}
@@ -132,7 +152,7 @@ func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) {
klog.Warningf("namespace %s role %s not found, but bind user %s", namespace, roleBinding.RoleRef.Name, username)
continue
} else {
klog.Errorln("get role", roleBinding.Namespace, roleBinding.RoleRef.Name, err)
klog.Errorln(err)
return nil, err
}
}
@@ -144,13 +164,13 @@ func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) {
return roles, nil
}
func GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.ClusterRole, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
func (am *amOperator) GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.ClusterRole, error) {
clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister()
clusterRoleBindingLister := am.informers.Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
klog.Errorln("get cluster role bindings", err)
klog.Errorln(err)
return nil, nil, err
}
@@ -164,7 +184,7 @@ func GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.Cluste
klog.Warningf("cluster role %s not found but bind user %s", clusterRoleBinding.RoleRef.Name, username)
continue
} else {
klog.Errorln("get cluster role", clusterRoleBinding.RoleRef.Name, err)
klog.Errorln(err)
return nil, nil, err
}
}
@@ -178,16 +198,16 @@ func GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.Cluste
return userFacingClusterRole, clusterRoles, nil
}
func GetUserClusterRole(username string) (*rbacv1.ClusterRole, error) {
userFacingClusterRole, _, err := GetUserClusterRoles(username)
func (am *amOperator) GetUserClusterRole(username string) (*rbacv1.ClusterRole, error) {
userFacingClusterRole, _, err := am.GetUserClusterRoles(username)
if err != nil {
return nil, err
}
return userFacingClusterRole, nil
}
func GetUserClusterRules(username string) ([]rbacv1.PolicyRule, error) {
_, clusterRoles, err := GetUserClusterRoles(username)
func (am *amOperator) GetUserClusterRules(username string) ([]rbacv1.PolicyRule, error) {
_, clusterRoles, err := am.GetUserClusterRoles(username)
if err != nil {
return nil, err
@@ -201,8 +221,8 @@ func GetUserClusterRules(username string) ([]rbacv1.PolicyRule, error) {
return rules, nil
}
func GetUserRules(namespace, username string) ([]rbacv1.PolicyRule, error) {
roles, err := GetUserRoles(namespace, username)
func (am *amOperator) GetUserRules(namespace, username string) ([]rbacv1.PolicyRule, error) {
roles, err := am.GetUserRoles(namespace, username)
if err != nil {
return nil, err
@@ -216,9 +236,9 @@ func GetUserRules(namespace, username string) ([]rbacv1.PolicyRule, error) {
return rules, nil
}
func GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.ClusterRoleBinding, error) {
func (am *amOperator) GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.ClusterRoleBinding, error) {
clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
clusterRoleBindings, err := am.informers.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
if err != nil {
klog.Errorln("get cluster role bindings", err)
@@ -236,17 +256,17 @@ func GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.ClusterRoleBinding, e
return result, nil
}
func GetWorkspaceRole(workspace, role string) (*rbacv1.ClusterRole, error) {
func (am *amOperator) GetWorkspaceRole(workspace, role string) (*rbacv1.ClusterRole, error) {
if !sliceutil.HasString(constants.WorkSpaceRoles, role) {
return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role)
}
role = fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(role)
return am.informers.Rbac().V1().ClusterRoles().Lister().Get(role)
}
func GetUserWorkspaceRoleMap(username string) (map[string]string, error) {
func (am *amOperator) GetUserWorkspaceRoleMap(username string) (map[string]string, error) {
clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
clusterRoleBindings, err := am.informers.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
if err != nil {
klog.Errorln("get cluster role bindings", err)
@@ -265,26 +285,26 @@ func GetUserWorkspaceRoleMap(username string) (map[string]string, error) {
return result, nil
}
func GetUserWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error) {
workspaceRoleMap, err := GetUserWorkspaceRoleMap(username)
func (am *amOperator) GetUserWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error) {
workspaceRoleMap, err := am.GetUserWorkspaceRoleMap(username)
if err != nil {
return nil, err
}
if workspaceRole := workspaceRoleMap[workspace]; workspaceRole != "" {
return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(workspaceRole)
return am.informers.Rbac().V1().ClusterRoles().Lister().Get(workspaceRole)
}
return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace user"}, username)
}
func GetRoleBindings(namespace string, roleName string) ([]*rbacv1.RoleBinding, error) {
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
func (am *amOperator) GetRoleBindings(namespace string, roleName string) ([]*rbacv1.RoleBinding, error) {
roleBindingLister := am.informers.Rbac().V1().RoleBindings().Lister()
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
klog.Errorln("get role bindings", namespace, err)
klog.Errorln(err)
return nil, err
}
@@ -301,12 +321,12 @@ func GetRoleBindings(namespace string, roleName string) ([]*rbacv1.RoleBinding,
return items, nil
}
func GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.ClusterRoleBinding, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
func (am *amOperator) GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.ClusterRoleBinding, error) {
clusterRoleBindingLister := am.informers.Rbac().V1().ClusterRoleBindings().Lister()
roleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
klog.Errorln("get cluster role bindings", err)
klog.Errorln(err)
return nil, err
}
@@ -321,14 +341,14 @@ func GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.ClusterRoleBindin
return items, nil
}
func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
func (am *amOperator) ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
roleBindings, err := GetClusterRoleBindings(clusterRoleName)
roleBindings, err := am.GetClusterRoleBindings(clusterRoleName)
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
users := make([]*User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
@@ -337,7 +357,7 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions,
continue
}
if err != nil {
klog.Errorln("get user info", subject.Name, err)
klog.Errorln(err)
return nil, err
}
users = append(users, user)
@@ -348,9 +368,7 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions,
// order & reverse
sort.Slice(users, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
i, j = j, i
}
switch orderBy {
default:
@@ -372,43 +390,28 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions,
}
func RoleUsers(namespace string, roleName string) ([]*models.User, error) {
roleBindings, err := GetRoleBindings(namespace, roleName)
func (am *amOperator) ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
return am.resources.ListResources(namespace, v1alpha2.Roles, conditions, orderBy, reverse, limit, offset)
}
func (am *amOperator) ListRoleBindings(namespace string, role string) ([]*rbacv1.RoleBinding, error) {
rbs, err := am.informers.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := GetUserInfo(subject.Name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
user.Role = roleBinding.RoleRef.Name
users = append(users, user)
}
result := make([]*rbacv1.RoleBinding, 0)
for _, rb := range rbs {
if rb.RoleRef.Name == role {
result = append(result, rb.DeepCopy())
}
}
return users, nil
return result, nil
}
func ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
return resources.ListResources(namespace, v1alpha2.Roles, conditions, orderBy, reverse, limit, offset)
}
func ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
func (am *amOperator) ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
conditions.Match[v1alpha2.OwnerName] = workspace
conditions.Match[v1alpha2.OwnerKind] = "Workspace"
result, err := resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset)
result, err := am.resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset)
if err != nil {
return nil, err
@@ -424,23 +427,23 @@ func ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy
return result, nil
}
func ListClusterRoles(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
return resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset)
func (am *amOperator) ListClusterRoles(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
return am.resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset)
}
func NamespaceUsers(namespaceName string) ([]*models.User, error) {
namespace, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(namespaceName)
func (am *amOperator) NamespaceUsers(namespaceName string) ([]*User, error) {
namespace, err := am.informers.Core().V1().Namespaces().Lister().Get(namespaceName)
if err != nil {
klog.Errorln("get namespace", namespaceName, err)
klog.Errorln(err)
return nil, err
}
roleBindings, err := GetRoleBindings(namespaceName, "")
roleBindings, err := am.GetRoleBindings(namespaceName, "")
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
users := make([]*User, 0)
for _, roleBinding := range roleBindings {
// controlled by ks-controller-manager
@@ -475,8 +478,8 @@ func NamespaceUsers(namespaceName string) ([]*models.User, error) {
return users, nil
}
func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRule, error) {
clusterRules, err := GetUserClusterRules(username)
func (am *amOperator) GetUserWorkspaceSimpleRules(workspace, username string) ([]SimpleRule, error) {
clusterRules, err := am.GetUserClusterRules(username)
if err != nil {
return nil, err
}
@@ -487,10 +490,10 @@ func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRul
APIGroups: []string{"*"},
Resources: []string{"*"},
}) {
return GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil
return am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil
}
workspaceRole, err := GetUserWorkspaceRole(workspace, username)
workspaceRole, err := am.GetUserWorkspaceRole(workspace, username)
if err != nil {
if apierrors.IsNotFound(err) {
@@ -501,26 +504,26 @@ func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRul
APIGroups: []string{"*"},
Resources: []string{"workspaces", "workspaces/*"},
}) {
return GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil
return am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil
}
return []models.SimpleRule{}, nil
return []SimpleRule{}, nil
}
klog.Error(err)
return nil, err
}
return GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil
return am.GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil
}
func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule {
func (am *amOperator) GetWorkspaceRoleSimpleRules(workspace, roleName string) []SimpleRule {
workspaceRules := make([]models.SimpleRule, 0)
workspaceRules := make([]SimpleRule, 0)
switch roleName {
case constants.WorkspaceAdmin:
workspaceRules = []models.SimpleRule{
workspaceRules = []SimpleRule{
{Name: "workspaces", Actions: []string{"edit", "delete", "view"}},
{Name: "members", Actions: []string{"edit", "delete", "create", "view"}},
{Name: "devops", Actions: []string{"edit", "delete", "create", "view"}},
@@ -530,7 +533,7 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule
{Name: "repos", Actions: []string{"view", "manage"}},
}
case constants.WorkspaceRegular:
workspaceRules = []models.SimpleRule{
workspaceRules = []SimpleRule{
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view", "create"}},
{Name: "projects", Actions: []string{"view", "create"}},
@@ -538,7 +541,7 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule
{Name: "repos", Actions: []string{"view"}},
}
case constants.WorkspaceViewer:
workspaceRules = []models.SimpleRule{
workspaceRules = []SimpleRule{
{Name: "workspaces", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
@@ -548,7 +551,7 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule
{Name: "repos", Actions: []string{"view"}},
}
case constants.WorkspacesManager:
workspaceRules = []models.SimpleRule{
workspaceRules = []SimpleRule{
{Name: "workspaces", Actions: []string{"edit", "delete", "view"}},
{Name: "members", Actions: []string{"edit", "delete", "create", "view"}},
{Name: "roles", Actions: []string{"view"}},
@@ -559,33 +562,33 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule
}
// Convert cluster role to rules
func GetClusterRoleSimpleRules(clusterRoleName string) ([]models.SimpleRule, error) {
func (am *amOperator) GetClusterRoleSimpleRules(clusterRoleName string) ([]SimpleRule, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister()
clusterRole, err := clusterRoleLister.Get(clusterRoleName)
if err != nil {
klog.Errorln("get cluster role", clusterRoleName, clusterRoleName)
klog.Errorln(err)
return nil, err
}
return getClusterSimpleRule(clusterRole.Rules), nil
}
func GetUserClusterSimpleRules(username string) ([]models.SimpleRule, error) {
clusterRules, err := GetUserClusterRules(username)
func (am *amOperator) GetUserClusterSimpleRules(username string) ([]SimpleRule, error) {
clusterRules, err := am.GetUserClusterRules(username)
if err != nil {
return nil, err
}
return getClusterSimpleRule(clusterRules), nil
}
func GetUserNamespaceSimpleRules(namespace, username string) ([]models.SimpleRule, error) {
clusterRules, err := GetUserClusterRules(username)
func (am *amOperator) GetUserNamespaceSimpleRules(namespace, username string) ([]SimpleRule, error) {
clusterRules, err := am.GetUserClusterRules(username)
if err != nil {
return nil, err
}
rules, err := GetUserRules(namespace, username)
rules, err := am.GetUserRules(namespace, username)
if err != nil {
return nil, err
}
@@ -595,21 +598,21 @@ func GetUserNamespaceSimpleRules(namespace, username string) ([]models.SimpleRul
}
// Convert roles to rules
func GetRoleSimpleRules(namespace string, roleName string) ([]models.SimpleRule, error) {
func (am *amOperator) GetRoleSimpleRules(namespace string, roleName string) ([]SimpleRule, error) {
roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
roleLister := am.informers.Rbac().V1().Roles().Lister()
role, err := roleLister.Roles(namespace).Get(roleName)
if err != nil {
klog.Errorln("get role", namespace, roleName, err)
klog.Errorln(err)
return nil, err
}
return getSimpleRule(role.Rules), nil
}
func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule {
rules := make([]models.SimpleRule, 0)
func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule {
rules := make([]SimpleRule, 0)
for i := 0; i < len(policy.ClusterRoleRuleMapping); i++ {
validActions := make([]string, 0)
@@ -619,17 +622,17 @@ func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule {
}
}
if len(validActions) > 0 {
rules = append(rules, models.SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions})
rules = append(rules, SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions})
}
}
return rules
}
func getSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule {
simpleRules := make([]models.SimpleRule, 0)
func getSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule {
simpleRules := make([]SimpleRule, 0)
for i := 0; i < len(policy.RoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.RoleRuleMapping[i].Name}
rule := SimpleRule{Name: policy.RoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < len(policy.RoleRuleMapping[i].Actions); j++ {
if rulesMatchesAction(policyRules, policy.RoleRuleMapping[i].Actions[j]) {
@@ -643,13 +646,13 @@ func getSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule {
return simpleRules
}
func CreateClusterRoleBinding(username string, clusterRoleName string) error {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
func (am *amOperator) CreateClusterRoleBinding(username string, clusterRoleName string) error {
clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister()
_, err := clusterRoleLister.Get(clusterRoleName)
if err != nil {
klog.Errorln("get cluster role", clusterRoleName, err)
klog.Errorln(err)
return err
}
@@ -671,7 +674,7 @@ func CreateClusterRoleBinding(username string, clusterRoleName string) error {
clusterRoleBinding.RoleRef = rbacv1.RoleRef{Name: clusterRoleName, Kind: ClusterRoleKind}
clusterRoleBinding.Subjects = []rbacv1.Subject{{Kind: rbacv1.UserKind, Name: username}}
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindingLister := am.informers.Rbac().V1().ClusterRoleBindings().Lister()
found, err := clusterRoleBindingLister.Get(username)
if apierrors.IsNotFound(err) {
@@ -714,159 +717,3 @@ func CreateClusterRoleBinding(username string, clusterRoleName string) error {
return nil
}
func RulesMatchesRequired(rules []rbacv1.PolicyRule, required rbacv1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}
func rulesMatchesAction(rules []rbacv1.PolicyRule, action models.Action) bool {
for _, required := range action.Rules {
if !RulesMatchesRequired(rules, required) {
return false
}
}
return true
}
func ruleMatchesRequired(rule rbacv1.PolicyRule, required rbacv1.PolicyRule) bool {
if len(required.NonResourceURLs) == 0 {
for _, apiGroup := range required.APIGroups {
for _, resource := range required.Resources {
resources := strings.Split(resource, "/")
resource = resources[0]
var subsource string
if len(resources) > 1 {
subsource = resources[1]
}
if len(required.ResourceNames) == 0 {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
return false
}
}
} else {
for _, resourceName := range required.ResourceNames {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
return false
}
}
}
}
}
}
} else {
for _, apiGroup := range required.APIGroups {
for _, nonResourceURL := range required.NonResourceURLs {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
return false
}
}
}
}
}
return true
}
func ruleMatchesResources(rule rbacv1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, rbacv1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == rbacv1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule rbacv1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, rbacv1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule rbacv1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
// Allow wildcard match
if spec == "*" {
return true
}
// Allow exact match
if spec == path {
return true
}
// Allow a trailing * subpath match
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func hasString(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,22 +21,21 @@ package policy
import (
"encoding/json"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"k8s.io/api/rbac/v1"
)
const (
rulesConfigPath = "/etc/kubesphere/rules/rules.json"
clusterRulesConfigPath = "/etc/kubesphere/rules/clusterrules.json"
rulesConfigPath = iam.ConfigPath + "/rules.json"
clusterRulesConfigPath = iam.ConfigPath + "/clusterrules.json"
)
func init() {
rulesConfig, err := ioutil.ReadFile(rulesConfigPath)
if err == nil {
config := &[]models.Rule{}
config := &[]iam.Rule{}
json.Unmarshal(rulesConfig, config)
if len(*config) > 0 {
RoleRuleMapping = *config
@@ -46,7 +45,7 @@ func init() {
clusterRulesConfig, err := ioutil.ReadFile(clusterRulesConfigPath)
if err == nil {
config := &[]models.Rule{}
config := &[]iam.Rule{}
json.Unmarshal(clusterRulesConfig, config)
if len(*config) > 0 {
ClusterRoleRuleMapping = *config
@@ -55,9 +54,9 @@ func init() {
}
var (
ClusterRoleRuleMapping = []models.Rule{
ClusterRoleRuleMapping = []iam.Rule{
{Name: "workspaces",
Actions: []models.Action{
Actions: []iam.Action{
{
Name: "manage",
Rules: []v1.PolicyRule{
@@ -72,7 +71,7 @@ var (
},
{
Name: "monitoring",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"get", "list"},
@@ -88,7 +87,7 @@ var (
},
{
Name: "alerting",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"get", "list"},
@@ -114,7 +113,7 @@ var (
},
{
Name: "logging",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"get", "list"},
@@ -126,7 +125,7 @@ var (
},
{
Name: "accounts",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -193,7 +192,7 @@ var (
},
}, {
Name: "roles",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -239,7 +238,7 @@ var (
},
}, {
Name: "storageclasses",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -283,7 +282,7 @@ var (
},
}, {
Name: "nodes",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -314,7 +313,7 @@ var (
},
}, {
Name: "repos",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -354,7 +353,7 @@ var (
},
}, {
Name: "apps",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -367,7 +366,7 @@ var (
},
}, {
Name: "components",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -380,9 +379,9 @@ var (
},
}}
RoleRuleMapping = []models.Rule{{
RoleRuleMapping = []iam.Rule{{
Name: "projects",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -419,7 +418,7 @@ var (
},
{
Name: "monitoring",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"get", "list"},
@@ -436,7 +435,7 @@ var (
{
Name: "alerting",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"get", "list"},
@@ -462,7 +461,7 @@ var (
},
{
Name: "members",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -508,7 +507,7 @@ var (
},
{
Name: "roles",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -549,7 +548,7 @@ var (
},
{
Name: "deployments",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -605,7 +604,7 @@ var (
},
}, {
Name: "statefulsets",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -659,7 +658,7 @@ var (
},
}, {
Name: "daemonsets",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -704,7 +703,7 @@ var (
},
}, {
Name: "pods",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "terminal",
Rules: []v1.PolicyRule{
{
@@ -736,7 +735,7 @@ var (
},
{
Name: "services",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -778,7 +777,7 @@ var (
},
{
Name: "internet",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -820,7 +819,7 @@ var (
{
Name: "routes",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -860,7 +859,7 @@ var (
},
}, {
Name: "volumes",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -900,7 +899,7 @@ var (
},
}, {
Name: "applications",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
@@ -954,7 +953,7 @@ var (
},
{
Name: "jobs",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
@@ -987,7 +986,7 @@ var (
},
{
Name: "cronjobs",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
@@ -1020,7 +1019,7 @@ var (
},
{
Name: "secrets",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
@@ -1053,7 +1052,7 @@ var (
},
{
Name: "configmaps",
Actions: []models.Action{
Actions: []iam.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},

61
pkg/models/iam/types.go Normal file
View File

@@ -0,0 +1,61 @@
/*
*
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package iam
import (
"k8s.io/api/rbac/v1"
"time"
)
const (
ConfigPath = "/etc/kubesphere/iam"
KindTokenReview = "TokenReview"
)
type Action struct {
Name string `json:"name"`
Rules []v1.PolicyRule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Actions []Action `json:"actions"`
}
type SimpleRule struct {
Name string `json:"name" description:"rule name"`
Actions []string `json:"actions" description:"actions"`
}
type RoleList struct {
ClusterRoles []*v1.ClusterRole `json:"clusterRole" description:"cluster role list"`
Roles []*v1.Role `json:"roles" description:"role list"`
}
type Config struct {
adminEmail string
adminPassword string
authRateLimit string
maxAuthFailed int
authTimeInterval time.Duration
tokenIdleTimeout time.Duration
userInitFile string
enableMultiLogin bool
generateKubeConfig bool
}

180
pkg/models/iam/utils.go Normal file
View File

@@ -0,0 +1,180 @@
/*
*
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package iam
import (
rbacv1 "k8s.io/api/rbac/v1"
"strings"
)
func RulesMatchesRequired(rules []rbacv1.PolicyRule, required rbacv1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}
func rulesMatchesAction(rules []rbacv1.PolicyRule, action Action) bool {
for _, required := range action.Rules {
if !RulesMatchesRequired(rules, required) {
return false
}
}
return true
}
func ruleMatchesRequired(rule rbacv1.PolicyRule, required rbacv1.PolicyRule) bool {
if len(required.NonResourceURLs) == 0 {
for _, apiGroup := range required.APIGroups {
for _, resource := range required.Resources {
resources := strings.Split(resource, "/")
resource = resources[0]
var subsource string
if len(resources) > 1 {
subsource = resources[1]
}
if len(required.ResourceNames) == 0 {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
return false
}
}
} else {
for _, resourceName := range required.ResourceNames {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
return false
}
}
}
}
}
}
} else {
for _, apiGroup := range required.APIGroups {
for _, nonResourceURL := range required.NonResourceURLs {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
return false
}
}
}
}
}
return true
}
func ruleMatchesResources(rule rbacv1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, rbacv1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == rbacv1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule rbacv1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, rbacv1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule rbacv1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
// Allow wildcard match
if spec == "*" {
return true
}
// Allow exact match
if spec == path {
return true
}
// Allow a trailing * subpath match
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func hasString(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}

View File

@@ -1,21 +1,21 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package application
*
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package openpitrix
import (
"fmt"
@@ -26,46 +26,39 @@ import (
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/informers"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/openpitrix/type"
"kubesphere.io/kubesphere/pkg/models/openpitrix/utils"
"kubesphere.io/kubesphere/pkg/server/params"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"openpitrix.io/openpitrix/pkg/pb"
"strings"
)
type Interface interface {
List(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error)
Get(namespace, clusterID string) (*Application, error)
Create(namespace string, request types.CreateClusterRequest) error
Patch(request types.ModifyClusterAttributesRequest) error
Delete(id string) error
type ApplicationInterface interface {
ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error)
DescribeApplication(namespace, clusterId string) (*Application, error)
CreateApplication(namespace string, request CreateClusterRequest) error
ModifyApplication(request ModifyClusterAttributesRequest) error
DeleteApplication(id string) error
}
type applicationOperator struct {
informers informers.SharedInformerFactory
opClient pb.ClusterManagerClient
opClient openpitrix.Client
}
func NewApplicaitonOperator(informers informers.SharedInformerFactory, client pb.ClusterManagerClient) Interface {
return &applicationOperator{
informers: informers,
opClient: client,
}
func newApplicationOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) ApplicationInterface {
return &applicationOperator{informers: informers, opClient: opClient}
}
type Application struct {
Name string `json:"name" description:"application name"`
Cluster *types.Cluster `json:"cluster,omitempty" description:"application cluster info"`
Version *types.AppVersion `json:"version,omitempty" description:"application template version info"`
App *types.App `json:"app,omitempty" description:"application template info"`
Cluster *Cluster `json:"cluster,omitempty" description:"application cluster info"`
Version *AppVersion `json:"version,omitempty" description:"application template version info"`
App *App `json:"app,omitempty" description:"application template info"`
WorkLoads *workLoads `json:"workloads,omitempty" description:"application workloads"`
Services []v1.Service `json:"services,omitempty" description:"application services"`
Ingresses []v1beta1.Ingress `json:"ingresses,omitempty" description:"application ingresses"`
@@ -77,35 +70,30 @@ type workLoads struct {
Daemonsets []appsv1.DaemonSet `json:"daemonsets,omitempty" description:"daemonset list"`
}
func (c *applicationOperator) List(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *applicationOperator) ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) {
describeClustersRequest := &pb.DescribeClustersRequest{
Limit: uint32(limit),
Offset: uint32(offset)}
if keyword := conditions.Match["keyword"]; keyword != "" {
if keyword := conditions.Match[Keyword]; keyword != "" {
describeClustersRequest.SearchWord = &wrappers.StringValue{Value: keyword}
}
if runtimeId := conditions.Match["runtime_id"]; runtimeId != "" {
if runtimeId := conditions.Match[RuntimeId]; runtimeId != "" {
describeClustersRequest.RuntimeId = []string{runtimeId}
}
if appId := conditions.Match["app_id"]; appId != "" {
if appId := conditions.Match[AppId]; appId != "" {
describeClustersRequest.AppId = []string{appId}
}
if versionId := conditions.Match["version_id"]; versionId != "" {
if versionId := conditions.Match[VersionId]; versionId != "" {
describeClustersRequest.VersionId = []string{versionId}
}
if status := conditions.Match["status"]; status != "" {
if status := conditions.Match[Status]; status != "" {
describeClustersRequest.Status = strings.Split(status, "|")
}
if orderBy != "" {
describeClustersRequest.SortKey = &wrappers.StringValue{Value: orderBy}
}
describeClustersRequest.Reverse = &wrappers.BoolValue{Value: !reverse}
resp, err := client.Cluster().DescribeClusters(openpitrix.SystemContext(), describeClustersRequest)
describeClustersRequest.Reverse = &wrappers.BoolValue{Value: reverse}
resp, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), describeClustersRequest)
if err != nil {
klog.Errorln(err)
return nil, err
@@ -125,34 +113,29 @@ func (c *applicationOperator) List(conditions *params.Conditions, limit, offset
}
func (c *applicationOperator) describeApplication(cluster *pb.Cluster) (*Application, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
var app Application
app.Name = cluster.Name.Value
app.Cluster = utils.ConvertCluster(cluster)
versionInfo, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}})
app.Cluster = convertCluster(cluster)
versionInfo, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}})
if err != nil {
klog.Errorln(err)
return nil, err
}
if len(versionInfo.AppVersionSet) > 0 {
app.Version = utils.ConvertAppVersion(versionInfo.AppVersionSet[0])
app.Version = convertAppVersion(versionInfo.AppVersionSet[0])
}
appInfo, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1})
appInfo, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1})
if err != nil {
klog.Errorln(err)
return nil, err
}
if len(appInfo.AppSet) > 0 {
app.App = utils.ConvertApp(appInfo.GetAppSet()[0])
app.App = convertApp(appInfo.GetAppSet()[0])
}
return &app, nil
}
func (c *applicationOperator) Get(namespace string, clusterId string) (*Application, error) {
func (c *applicationOperator) DescribeApplication(namespace string, clusterId string) (*Application, error) {
clusters, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), &pb.DescribeClustersRequest{ClusterId: []string{clusterId}, Limit: 1})
@@ -280,7 +263,7 @@ func (c *applicationOperator) getLabels(namespace string, workloads *workLoads)
return &workloadLabels
}
func (c *applicationOperator) isExist(svcs []v1.Service, svc v1.Service) bool {
func (c *applicationOperator) isExist(svcs []v1.Service, svc *v1.Service) bool {
for _, item := range svcs {
if item.Name == svc.Name && item.Namespace == svc.Namespace {
return true
@@ -293,17 +276,16 @@ func (c *applicationOperator) getSvcs(namespace string, workLoadLabels *[]map[st
if len(*workLoadLabels) == 0 {
return nil
}
k8sClient := cs.ClientSets().K8s().Kubernetes()
var services []v1.Service
for _, label := range *workLoadLabels {
labelSelector := labels.Set(label).AsSelector().String()
svcs, err := k8sClient.CoreV1().Services(namespace).List(metav1.ListOptions{LabelSelector: labelSelector})
labelSelector := labels.Set(label).AsSelector()
svcs, err := c.informers.Core().V1().Services().Lister().Services(namespace).List(labelSelector)
if err != nil {
klog.Errorf("get app's svc failed, reason: %v", err)
}
for _, item := range svcs.Items {
for _, item := range svcs {
if !c.isExist(services, item) {
services = append(services, item)
services = append(services, *item)
}
}
}
@@ -352,7 +334,7 @@ func (c *applicationOperator) getIng(namespace string, services []v1.Service) []
return ings
}
func (c *applicationOperator) Create(namespace string, request types.CreateClusterRequest) error {
func (c *applicationOperator) CreateApplication(namespace string, request CreateClusterRequest) error {
ns, err := c.informers.Core().V1().Namespaces().Lister().Get(namespace)
if err != nil {
klog.Error(err)
@@ -365,14 +347,7 @@ func (c *applicationOperator) Create(namespace string, request types.CreateClust
return fmt.Errorf("runtime not init: namespace %s", namespace)
}
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
_, err = client.Cluster().CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{
_, err = c.opClient.CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{
AppId: &wrappers.StringValue{Value: request.AppId},
VersionId: &wrappers.StringValue{Value: request.VersionId},
RuntimeId: &wrappers.StringValue{Value: request.RuntimeId},
@@ -387,13 +362,7 @@ func (c *applicationOperator) Create(namespace string, request types.CreateClust
return nil
}
func (c *applicationOperator) Patch(request types.ModifyClusterAttributesRequest) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *applicationOperator) ModifyApplication(request ModifyClusterAttributesRequest) error {
modifyClusterAttributesRequest := &pb.ModifyClusterAttributesRequest{ClusterId: &wrappers.StringValue{Value: request.ClusterID}}
if request.Name != nil {
@@ -403,16 +372,17 @@ func (c *applicationOperator) Patch(request types.ModifyClusterAttributesRequest
modifyClusterAttributesRequest.Description = &wrappers.StringValue{Value: *request.Description}
}
_, err = op.Cluster().ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest)
_, err := c.opClient.ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest)
if err != nil {
klog.Errorln(err)
return err
}
return nil
}
func (c *applicationOperator) Delete(clusterId string) error {
func (c *applicationOperator) DeleteApplication(clusterId string) error {
_, err := c.opClient.DeleteClusters(openpitrix.SystemContext(), &pb.DeleteClustersRequest{ClusterId: []string{clusterId}, Force: &wrappers.BoolValue{Value: true}})
if err != nil {

View File

@@ -0,0 +1,114 @@
/*
*
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package openpitrix
import (
"github.com/golang/mock/gomock"
"github.com/golang/protobuf/ptypes/wrappers"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"openpitrix.io/openpitrix/pkg/pb"
"testing"
)
func namespacesToRuntimeObjects(namespaces ...*v1.Namespace) []runtime.Object {
var objs []runtime.Object
for _, deploy := range namespaces {
objs = append(objs, deploy)
}
return objs
}
func TestApplicationOperator_CreateApplication(t *testing.T) {
tests := []struct {
description string
existNamespaces []*v1.Namespace
targetNamespace string
createClusterRequest CreateClusterRequest
expected error
}{
{
description: "create application test",
existNamespaces: []*v1.Namespace{{
ObjectMeta: metav1.ObjectMeta{Name: "test", Annotations: map[string]string{openpitrix.RuntimeAnnotationKey: "runtime-ncafface"}},
}},
targetNamespace: "test",
createClusterRequest: CreateClusterRequest{
Conf: "app-agwerl",
RuntimeId: "runtime-ncafface",
VersionId: "version-acklmalkds",
Username: "system",
},
expected: nil,
},
{
description: "create application test2",
existNamespaces: []*v1.Namespace{},
targetNamespace: "test2",
createClusterRequest: CreateClusterRequest{
Conf: "app-agwerl",
RuntimeId: "runtime-ncafface",
VersionId: "version-acklmalkds",
Username: "system",
},
expected: errors.NewNotFound(schema.GroupResource{Group: "", Resource: "namespace"}, "test2"),
},
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
for _, test := range tests {
op := openpitrix.NewMockClient(ctrl)
objs := namespacesToRuntimeObjects(test.existNamespaces...)
k8s := fake.NewSimpleClientset(objs...)
informer := informers.NewSharedInformerFactory(k8s, 0)
stopChan := make(chan struct{}, 0)
informer.Core().V1().Namespaces().Lister()
informer.Start(stopChan)
informer.WaitForCacheSync(stopChan)
applicationOperator := newApplicationOperator(informer, op)
// setup expect response
// op.EXPECT().CreateCluster(gomock.Any(), gomock.Any()).Return(&pb.CreateClusterResponse{}, nil).AnyTimes()
op.EXPECT().CreateCluster(openpitrix.ContextWithUsername(test.createClusterRequest.Username), &pb.CreateClusterRequest{
AppId: &wrappers.StringValue{Value: test.createClusterRequest.AppId},
VersionId: &wrappers.StringValue{Value: test.createClusterRequest.VersionId},
RuntimeId: &wrappers.StringValue{Value: test.createClusterRequest.RuntimeId},
Conf: &wrappers.StringValue{Value: test.createClusterRequest.Conf},
}).Return(&pb.CreateClusterResponse{}, nil).AnyTimes()
t.Run(test.description, func(t *testing.T) {
err := applicationOperator.CreateApplication(test.targetNamespace, test.createClusterRequest)
if err != nil && err.Error() != test.expected.Error() {
t.Error(err)
}
})
}
}

View File

@@ -1,6 +1,6 @@
/*
*
* Copyright 2019 The KubeSphere Authors.
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
* /
*/
package app
package openpitrix
import (
"github.com/go-openapi/strfmt"
@@ -25,41 +25,58 @@ import (
"google.golang.org/grpc/status"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/openpitrix/type"
"kubesphere.io/kubesphere/pkg/models/openpitrix/utils"
"kubesphere.io/kubesphere/pkg/server/params"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"openpitrix.io/openpitrix/pkg/pb"
"strings"
)
const (
BuiltinRepoId = "repo-helm"
StatusActive = "active"
)
type AppTemplateInterface interface {
ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
DescribeApp(id string) (*App, error)
DeleteApp(id string) error
CreateApp(request *CreateAppRequest) (*CreateAppResponse, error)
ModifyApp(appId string, request *ModifyAppRequest) error
DeleteAppVersion(id string) error
ModifyAppVersion(id string, request *ModifyAppVersionRequest) error
DescribeAppVersion(id string) (*AppVersion, error)
CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error)
ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error)
GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error)
DoAppAction(appId string, request *ActionRequest) error
DoAppVersionAction(versionId string, request *ActionRequest) error
GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error)
ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
}
func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
type appTemplateOperator struct {
opClient openpitrix.Client
}
func newAppTemplateOperator(opClient openpitrix.Client) AppTemplateInterface {
return &appTemplateOperator{
opClient: opClient,
}
}
func (c *appTemplateOperator) ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
describeAppsRequest := &pb.DescribeAppsRequest{}
if keyword := conditions.Match["keyword"]; keyword != "" {
if keyword := conditions.Match[Keyword]; keyword != "" {
describeAppsRequest.SearchWord = &wrappers.StringValue{Value: keyword}
}
if appId := conditions.Match["app_id"]; appId != "" {
if appId := conditions.Match[AppId]; appId != "" {
describeAppsRequest.AppId = strings.Split(appId, "|")
}
if isv := conditions.Match["isv"]; isv != "" {
if isv := conditions.Match[ISV]; isv != "" {
describeAppsRequest.Isv = strings.Split(isv, "|")
}
if categoryId := conditions.Match["category_id"]; categoryId != "" {
if categoryId := conditions.Match[CategoryId]; categoryId != "" {
describeAppsRequest.CategoryId = strings.Split(categoryId, "|")
}
if repoId := conditions.Match["repo"]; repoId != "" {
if repoId := conditions.Match[RepoId]; repoId != "" {
// hard code, app template in built-in repo has no repo_id attribute
if repoId == BuiltinRepoId {
describeAppsRequest.RepoId = []string{"\u0000"}
@@ -67,7 +84,7 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit
describeAppsRequest.RepoId = strings.Split(repoId, "|")
}
}
if status := conditions.Match["status"]; status != "" {
if status := conditions.Match[Status]; status != "" {
describeAppsRequest.Status = strings.Split(status, "|")
}
if orderBy != "" {
@@ -76,7 +93,7 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit
describeAppsRequest.Reverse = &wrappers.BoolValue{Value: reverse}
describeAppsRequest.Limit = uint32(limit)
describeAppsRequest.Offset = uint32(offset)
resp, err := client.App().DescribeApps(openpitrix.SystemContext(), describeAppsRequest)
resp, err := c.opClient.DescribeApps(openpitrix.SystemContext(), describeAppsRequest)
if err != nil {
klog.Error(err)
return nil, err
@@ -85,19 +102,14 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit
items := make([]interface{}, 0)
for _, item := range resp.AppSet {
items = append(items, utils.ConvertApp(item))
items = append(items, convertApp(item))
}
return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil
}
func DescribeApp(id string) (*types.App, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
resp, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{
func (c *appTemplateOperator) DescribeApp(id string) (*App, error) {
resp, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{
AppId: []string{id},
Limit: 1,
})
@@ -106,10 +118,10 @@ func DescribeApp(id string) (*types.App, error) {
return nil, err
}
var app *types.App
var app *App
if len(resp.AppSet) > 0 {
app = utils.ConvertApp(resp.AppSet[0])
app = convertApp(resp.AppSet[0])
return app, nil
} else {
err := status.New(codes.NotFound, "resource not found").Err()
@@ -118,13 +130,8 @@ func DescribeApp(id string) (*types.App, error) {
}
}
func DeleteApp(id string) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
_, err = op.App().DeleteApps(openpitrix.SystemContext(), &pb.DeleteAppsRequest{
func (c *appTemplateOperator) DeleteApp(id string) error {
_, err := c.opClient.DeleteApps(openpitrix.SystemContext(), &pb.DeleteAppsRequest{
AppId: []string{id},
})
if err != nil {
@@ -134,12 +141,7 @@ func DeleteApp(id string) error {
return nil
}
func CreateApp(request *types.CreateAppRequest) (*types.CreateAppResponse, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *appTemplateOperator) CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) {
createAppRequest := &pb.CreateAppRequest{
Name: &wrappers.StringValue{Value: request.Name},
VersionType: &wrappers.StringValue{Value: request.VersionType},
@@ -154,25 +156,18 @@ func CreateApp(request *types.CreateAppRequest) (*types.CreateAppResponse, error
if request.Isv != "" {
createAppRequest.Isv = &wrappers.StringValue{Value: request.Isv}
}
resp, err := op.App().CreateApp(openpitrix.ContextWithUsername(request.Username), createAppRequest)
resp, err := c.opClient.CreateApp(openpitrix.ContextWithUsername(request.Username), createAppRequest)
if err != nil {
klog.Error(err)
return nil, err
}
return &types.CreateAppResponse{
return &CreateAppResponse{
AppID: resp.GetAppId().GetValue(),
VersionID: resp.GetVersionId().GetValue(),
}, nil
}
func PatchApp(appId string, request *types.ModifyAppRequest) error {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *appTemplateOperator) ModifyApp(appId string, request *ModifyAppRequest) error {
// upload app attachment
if request.AttachmentContent != nil {
uploadAttachmentRequest := &pb.UploadAppAttachmentRequest{
@@ -186,7 +181,7 @@ func PatchApp(appId string, request *types.ModifyAppRequest) error {
uploadAttachmentRequest.Sequence = &wrappers.UInt32Value{Value: uint32(*request.Sequence)}
}
_, err := client.App().UploadAppAttachment(openpitrix.SystemContext(), uploadAttachmentRequest)
_, err := c.opClient.UploadAppAttachment(openpitrix.SystemContext(), uploadAttachmentRequest)
if err != nil {
klog.Error(err)
@@ -229,7 +224,7 @@ func PatchApp(appId string, request *types.ModifyAppRequest) error {
patchAppRequest.Tos = &wrappers.StringValue{Value: *request.Tos}
}
_, err = client.App().ModifyApp(openpitrix.SystemContext(), patchAppRequest)
_, err := c.opClient.ModifyApp(openpitrix.SystemContext(), patchAppRequest)
if err != nil {
klog.Error(err)
@@ -239,12 +234,7 @@ func PatchApp(appId string, request *types.ModifyAppRequest) error {
return nil
}
func CreateAppVersion(request *types.CreateAppVersionRequest) (*types.CreateAppVersionResponse, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *appTemplateOperator) CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error) {
createAppVersionRequest := &pb.CreateAppVersionRequest{
AppId: &wrappers.StringValue{Value: request.AppId},
Name: &wrappers.StringValue{Value: request.Name},
@@ -256,24 +246,17 @@ func CreateAppVersion(request *types.CreateAppVersionRequest) (*types.CreateAppV
createAppVersionRequest.Package = &wrappers.BytesValue{Value: request.Package}
}
resp, err := op.App().CreateAppVersion(openpitrix.ContextWithUsername(request.Username), createAppVersionRequest)
resp, err := c.opClient.CreateAppVersion(openpitrix.ContextWithUsername(request.Username), createAppVersionRequest)
if err != nil {
klog.Error(err)
return nil, err
}
return &types.CreateAppVersionResponse{
return &CreateAppVersionResponse{
VersionId: resp.GetVersionId().GetValue(),
}, nil
}
func ValidatePackage(request *types.ValidatePackageRequest) (*types.ValidatePackageResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *appTemplateOperator) ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error) {
r := &pb.ValidatePackageRequest{}
if request.VersionPackage != nil {
@@ -283,14 +266,14 @@ func ValidatePackage(request *types.ValidatePackageRequest) (*types.ValidatePack
r.VersionType = request.VersionType
}
resp, err := client.App().ValidatePackage(openpitrix.SystemContext(), r)
resp, err := c.opClient.ValidatePackage(openpitrix.SystemContext(), r)
if err != nil {
klog.Error(err)
return nil, err
}
result := &types.ValidatePackageResponse{}
result := &ValidatePackageResponse{}
if resp.Error != nil {
result.Error = resp.Error.Value
@@ -316,13 +299,8 @@ func ValidatePackage(request *types.ValidatePackageRequest) (*types.ValidatePack
return result, nil
}
func DeleteAppVersion(id string) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
_, err = op.App().DeleteAppVersion(openpitrix.SystemContext(), &pb.DeleteAppVersionRequest{
func (c *appTemplateOperator) DeleteAppVersion(id string) error {
_, err := c.opClient.DeleteAppVersion(openpitrix.SystemContext(), &pb.DeleteAppVersionRequest{
VersionId: &wrappers.StringValue{Value: id},
})
if err != nil {
@@ -332,12 +310,7 @@ func DeleteAppVersion(id string) error {
return nil
}
func PatchAppVersion(id string, request *types.ModifyAppVersionRequest) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *appTemplateOperator) ModifyAppVersion(id string, request *ModifyAppVersionRequest) error {
modifyAppVersionRequest := &pb.ModifyAppVersionRequest{
VersionId: &wrappers.StringValue{Value: id},
}
@@ -355,7 +328,7 @@ func PatchAppVersion(id string, request *types.ModifyAppVersionRequest) error {
modifyAppVersionRequest.PackageFiles = request.PackageFiles
}
_, err = op.App().ModifyAppVersion(openpitrix.SystemContext(), modifyAppVersionRequest)
_, err := c.opClient.ModifyAppVersion(openpitrix.SystemContext(), modifyAppVersionRequest)
if err != nil {
klog.Error(err)
return err
@@ -363,13 +336,8 @@ func PatchAppVersion(id string, request *types.ModifyAppVersionRequest) error {
return nil
}
func DescribeAppVersion(id string) (*types.AppVersion, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{
func (c *appTemplateOperator) DescribeAppVersion(id string) (*AppVersion, error) {
resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{
VersionId: []string{id},
Limit: 1,
})
@@ -378,10 +346,10 @@ func DescribeAppVersion(id string) (*types.AppVersion, error) {
return nil, err
}
var app *types.AppVersion
var app *AppVersion
if len(resp.AppVersionSet) > 0 {
app = utils.ConvertAppVersion(resp.AppVersionSet[0])
app = convertAppVersion(resp.AppVersionSet[0])
return app, nil
} else {
err := status.New(codes.NotFound, "resource not found").Err()
@@ -390,13 +358,8 @@ func DescribeAppVersion(id string) (*types.AppVersion, error) {
}
}
func GetAppVersionPackage(appId, versionId string) (*types.GetAppVersionPackageResponse, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
resp, err := op.App().GetAppVersionPackage(openpitrix.SystemContext(), &pb.GetAppVersionPackageRequest{
func (c *appTemplateOperator) GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error) {
resp, err := c.opClient.GetAppVersionPackage(openpitrix.SystemContext(), &pb.GetAppVersionPackageRequest{
VersionId: &wrappers.StringValue{Value: versionId},
})
if err != nil {
@@ -404,7 +367,7 @@ func GetAppVersionPackage(appId, versionId string) (*types.GetAppVersionPackageR
return nil, err
}
app := &types.GetAppVersionPackageResponse{
app := &GetAppVersionPackageResponse{
AppId: appId,
VersionId: versionId,
}
@@ -416,21 +379,14 @@ func GetAppVersionPackage(appId, versionId string) (*types.GetAppVersionPackageR
return app, nil
}
func DoAppAction(appId string, request *types.ActionRequest) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *appTemplateOperator) DoAppAction(appId string, request *ActionRequest) error {
switch request.Action {
case "recover":
case ActionRecover:
// TODO openpitrix need to implement app recover interface
resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{
resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{
AppId: []string{appId},
Status: []string{"suspended"},
Status: []string{StatusSuspended},
Limit: 200,
Offset: 0,
})
@@ -440,7 +396,7 @@ func DoAppAction(appId string, request *types.ActionRequest) error {
}
for _, version := range resp.AppVersionSet {
_, err = op.App().RecoverAppVersion(openpitrix.SystemContext(), &pb.RecoverAppVersionRequest{
_, err = c.opClient.RecoverAppVersion(openpitrix.SystemContext(), &pb.RecoverAppVersionRequest{
VersionId: version.VersionId,
})
if err != nil {
@@ -449,11 +405,11 @@ func DoAppAction(appId string, request *types.ActionRequest) error {
}
}
case "suspend":
case ActionSuspend:
// TODO openpitrix need to implement app suspend interface
resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{
resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{
AppId: []string{appId},
Status: []string{"active"},
Status: []string{StatusActive},
Limit: 200,
Offset: 0,
})
@@ -462,7 +418,7 @@ func DoAppAction(appId string, request *types.ActionRequest) error {
return err
}
for _, version := range resp.AppVersionSet {
_, err = op.App().SuspendAppVersion(openpitrix.SystemContext(), &pb.SuspendAppVersionRequest{
_, err = c.opClient.SuspendAppVersion(openpitrix.SystemContext(), &pb.SuspendAppVersionRequest{
VersionId: version.VersionId,
})
@@ -473,7 +429,7 @@ func DoAppAction(appId string, request *types.ActionRequest) error {
}
default:
err = status.New(codes.InvalidArgument, "action not support").Err()
err := status.New(codes.InvalidArgument, "action not support").Err()
klog.Error(err)
return err
}
@@ -481,42 +437,36 @@ func DoAppAction(appId string, request *types.ActionRequest) error {
return nil
}
func DoAppVersionAction(versionId string, request *types.ActionRequest) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *appTemplateOperator) DoAppVersionAction(versionId string, request *ActionRequest) error {
var err error
switch request.Action {
case "cancel":
_, err = op.App().CancelAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.CancelAppVersionRequest{
case ActionCancel:
_, err = c.opClient.CancelAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.CancelAppVersionRequest{
VersionId: &wrappers.StringValue{Value: versionId},
})
case "pass":
_, err = op.App().AdminPassAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.PassAppVersionRequest{
case ActionPass:
_, err = c.opClient.AdminPassAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.PassAppVersionRequest{
VersionId: &wrappers.StringValue{Value: versionId},
})
case "recover":
_, err = op.App().RecoverAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RecoverAppVersionRequest{
case ActionRecover:
_, err = c.opClient.RecoverAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RecoverAppVersionRequest{
VersionId: &wrappers.StringValue{Value: versionId},
})
case "reject":
_, err = op.App().AdminRejectAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RejectAppVersionRequest{
case ActionReject:
_, err = c.opClient.AdminRejectAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RejectAppVersionRequest{
VersionId: &wrappers.StringValue{Value: versionId},
Message: &wrappers.StringValue{Value: request.Message},
})
case "submit":
_, err = op.App().SubmitAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SubmitAppVersionRequest{
case ActionSubmit:
_, err = c.opClient.SubmitAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SubmitAppVersionRequest{
VersionId: &wrappers.StringValue{Value: versionId},
})
case "suspend":
_, err = op.App().SuspendAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SuspendAppVersionRequest{
case ActionSuspend:
_, err = c.opClient.SuspendAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SuspendAppVersionRequest{
VersionId: &wrappers.StringValue{Value: versionId},
})
case "release":
_, err = op.App().ReleaseAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.ReleaseAppVersionRequest{
case ActionRelease:
_, err = c.opClient.ReleaseAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.ReleaseAppVersionRequest{
VersionId: &wrappers.StringValue{Value: versionId},
})
default:
@@ -531,12 +481,7 @@ func DoAppVersionAction(versionId string, request *types.ActionRequest) error {
return nil
}
func GetAppVersionFiles(versionId string, request *types.GetAppVersionFilesRequest) (*types.GetAppVersionPackageFilesResponse, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *appTemplateOperator) GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error) {
getAppVersionPackageFilesRequest := &pb.GetAppVersionPackageFilesRequest{
VersionId: &wrappers.StringValue{Value: versionId},
}
@@ -544,13 +489,13 @@ func GetAppVersionFiles(versionId string, request *types.GetAppVersionFilesReque
getAppVersionPackageFilesRequest.Files = request.Files
}
resp, err := op.App().GetAppVersionPackageFiles(openpitrix.SystemContext(), getAppVersionPackageFilesRequest)
resp, err := c.opClient.GetAppVersionPackageFiles(openpitrix.SystemContext(), getAppVersionPackageFilesRequest)
if err != nil {
klog.Error(err)
return nil, err
}
version := &types.GetAppVersionPackageFilesResponse{
version := &GetAppVersionPackageFilesResponse{
VersionId: versionId,
}
@@ -564,14 +509,7 @@ func GetAppVersionFiles(versionId string, request *types.GetAppVersionFilesReque
return version, nil
}
func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *appTemplateOperator) ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
describeAppVersionAudits := &pb.DescribeAppVersionAuditsRequest{}
if keyword := conditions.Match["keyword"]; keyword != "" {
@@ -589,10 +527,10 @@ func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse
if orderBy != "" {
describeAppVersionAudits.SortKey = &wrappers.StringValue{Value: orderBy}
}
describeAppVersionAudits.Reverse = &wrappers.BoolValue{Value: !reverse}
describeAppVersionAudits.Reverse = &wrappers.BoolValue{Value: reverse}
describeAppVersionAudits.Limit = uint32(limit)
describeAppVersionAudits.Offset = uint32(offset)
resp, err := client.App().DescribeAppVersionAudits(openpitrix.SystemContext(), describeAppVersionAudits)
resp, err := c.opClient.DescribeAppVersionAudits(openpitrix.SystemContext(), describeAppVersionAudits)
if err != nil {
klog.Error(err)
@@ -602,21 +540,14 @@ func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse
items := make([]interface{}, 0)
for _, item := range resp.AppVersionAuditSet {
appVersion := utils.ConvertAppVersionAudit(item)
appVersion := convertAppVersionAudit(item)
items = append(items, appVersion)
}
return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil
}
func ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *appTemplateOperator) ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
describeAppVersionReviews := &pb.DescribeAppVersionReviewsRequest{}
if keyword := conditions.Match["keyword"]; keyword != "" {
@@ -628,11 +559,11 @@ func ListAppVersionReviews(conditions *params.Conditions, orderBy string, revers
if orderBy != "" {
describeAppVersionReviews.SortKey = &wrappers.StringValue{Value: orderBy}
}
describeAppVersionReviews.Reverse = &wrappers.BoolValue{Value: !reverse}
describeAppVersionReviews.Reverse = &wrappers.BoolValue{Value: reverse}
describeAppVersionReviews.Limit = uint32(limit)
describeAppVersionReviews.Offset = uint32(offset)
// TODO icon is needed
resp, err := client.App().DescribeAppVersionReviews(openpitrix.SystemContext(), describeAppVersionReviews)
resp, err := c.opClient.DescribeAppVersionReviews(openpitrix.SystemContext(), describeAppVersionReviews)
if err != nil {
klog.Error(err)
@@ -642,21 +573,14 @@ func ListAppVersionReviews(conditions *params.Conditions, orderBy string, revers
items := make([]interface{}, 0)
for _, item := range resp.AppVersionReviewSet {
appVersion := utils.ConvertAppVersionReview(item)
appVersion := convertAppVersionReview(item)
items = append(items, appVersion)
}
return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil
}
func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *appTemplateOperator) ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
describeAppVersionsRequest := &pb.DescribeAppVersionsRequest{}
if keyword := conditions.Match["keyword"]; keyword != "" {
@@ -671,10 +595,10 @@ func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool
if orderBy != "" {
describeAppVersionsRequest.SortKey = &wrappers.StringValue{Value: orderBy}
}
describeAppVersionsRequest.Reverse = &wrappers.BoolValue{Value: !reverse}
describeAppVersionsRequest.Reverse = &wrappers.BoolValue{Value: reverse}
describeAppVersionsRequest.Limit = uint32(limit)
describeAppVersionsRequest.Offset = uint32(offset)
resp, err := client.App().DescribeAppVersions(openpitrix.SystemContext(), describeAppVersionsRequest)
resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), describeAppVersionsRequest)
if err != nil {
klog.Error(err)
@@ -684,7 +608,7 @@ func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool
items := make([]interface{}, 0)
for _, item := range resp.AppVersionSet {
appVersion := utils.ConvertAppVersion(item)
appVersion := convertAppVersion(item)
items = append(items, appVersion)
}

View File

@@ -16,26 +16,32 @@
* /
*/
package attachment
package openpitrix
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/models/openpitrix/type"
"kubesphere.io/kubesphere/pkg/models/openpitrix/utils"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"openpitrix.io/openpitrix/pkg/pb"
)
func DescribeAttachment(id string) (*types.Attachment, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
type AttachmentInterface interface {
DescribeAttachment(id string) (*Attachment, error)
}
type attachmentOperator struct {
opClient openpitrix.Client
}
func newAttachmentOperator(opClient openpitrix.Client) AttachmentInterface {
return &attachmentOperator{
opClient: opClient,
}
resp, err := op.Attachment().GetAttachments(openpitrix.SystemContext(), &pb.GetAttachmentsRequest{
}
func (c *attachmentOperator) DescribeAttachment(id string) (*Attachment, error) {
resp, err := c.opClient.GetAttachments(openpitrix.SystemContext(), &pb.GetAttachmentsRequest{
AttachmentId: []string{id},
})
if err != nil {
@@ -43,7 +49,7 @@ func DescribeAttachment(id string) (*types.Attachment, error) {
return nil, err
}
if len(resp.Attachments) > 0 {
return utils.ConvertAttachment(resp.Attachments[id]), nil
return convertAttachment(resp.Attachments[id]), nil
} else {
err := status.New(codes.NotFound, "resource not found").Err()
klog.Error(err)

View File

@@ -1,6 +1,6 @@
/*
*
* Copyright 2019 The KubeSphere Authors.
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
* /
*/
package category
package openpitrix
import (
"github.com/golang/protobuf/ptypes/wrappers"
@@ -24,20 +24,30 @@ import (
"google.golang.org/grpc/status"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/openpitrix/type"
"kubesphere.io/kubesphere/pkg/models/openpitrix/utils"
"kubesphere.io/kubesphere/pkg/server/params"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"openpitrix.io/openpitrix/pkg/pb"
)
func CreateCategory(request *types.CreateCategoryRequest) (*types.CreateCategoryResponse, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
type CategoryInterface interface {
CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error)
DeleteCategory(id string) error
ModifyCategory(id string, request *ModifyCategoryRequest) error
ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
DescribeCategory(id string) (*Category, error)
}
type categoryOperator struct {
opClient openpitrix.Client
}
func newCategoryOperator(opClient openpitrix.Client) CategoryInterface {
return &categoryOperator{
opClient: opClient,
}
}
func (c *categoryOperator) CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error) {
r := &pb.CreateCategoryRequest{
Name: &wrappers.StringValue{Value: request.Name},
Locale: &wrappers.StringValue{Value: request.Locale},
@@ -47,23 +57,18 @@ func CreateCategory(request *types.CreateCategoryRequest) (*types.CreateCategory
r.Icon = &wrappers.BytesValue{Value: request.Icon}
}
resp, err := op.Category().CreateCategory(openpitrix.SystemContext(), r)
resp, err := c.opClient.CreateCategory(openpitrix.SystemContext(), r)
if err != nil {
klog.Error(err)
return nil, err
}
return &types.CreateCategoryResponse{
return &CreateCategoryResponse{
CategoryId: resp.GetCategoryId().GetValue(),
}, nil
}
func DeleteCategory(id string) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
_, err = op.Category().DeleteCategories(openpitrix.SystemContext(), &pb.DeleteCategoriesRequest{
func (c *categoryOperator) DeleteCategory(id string) error {
_, err := c.opClient.DeleteCategories(openpitrix.SystemContext(), &pb.DeleteCategoriesRequest{
CategoryId: []string{id},
})
if err != nil {
@@ -73,12 +78,7 @@ func DeleteCategory(id string) error {
return nil
}
func PatchCategory(id string, request *types.ModifyCategoryRequest) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *categoryOperator) ModifyCategory(id string, request *ModifyCategoryRequest) error {
modifyCategoryRequest := &pb.ModifyCategoryRequest{
CategoryId: &wrappers.StringValue{Value: id},
}
@@ -95,7 +95,7 @@ func PatchCategory(id string, request *types.ModifyCategoryRequest) error {
modifyCategoryRequest.Icon = &wrappers.BytesValue{Value: request.Icon}
}
_, err = op.Category().ModifyCategory(openpitrix.SystemContext(), modifyCategoryRequest)
_, err := c.opClient.ModifyCategory(openpitrix.SystemContext(), modifyCategoryRequest)
if err != nil {
klog.Error(err)
return err
@@ -103,13 +103,8 @@ func PatchCategory(id string, request *types.ModifyCategoryRequest) error {
return nil
}
func DescribeCategory(id string) (*types.Category, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
resp, err := op.Category().DescribeCategories(openpitrix.SystemContext(), &pb.DescribeCategoriesRequest{
func (c *categoryOperator) DescribeCategory(id string) (*Category, error) {
resp, err := c.opClient.DescribeCategories(openpitrix.SystemContext(), &pb.DescribeCategoriesRequest{
CategoryId: []string{id},
Limit: 1,
})
@@ -118,10 +113,10 @@ func DescribeCategory(id string) (*types.Category, error) {
return nil, err
}
var category *types.Category
var category *Category
if len(resp.CategorySet) > 0 {
category = utils.ConvertCategory(resp.CategorySet[0])
category = convertCategory(resp.CategorySet[0])
return category, nil
} else {
err := status.New(codes.NotFound, "resource not found").Err()
@@ -130,26 +125,19 @@ func DescribeCategory(id string) (*types.Category, error) {
}
}
func ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *categoryOperator) ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
req := &pb.DescribeCategoriesRequest{}
if keyword := conditions.Match["keyword"]; keyword != "" {
if keyword := conditions.Match[Keyword]; keyword != "" {
req.SearchWord = &wrappers.StringValue{Value: keyword}
}
if orderBy != "" {
req.SortKey = &wrappers.StringValue{Value: orderBy}
}
req.Reverse = &wrappers.BoolValue{Value: !reverse}
req.Reverse = &wrappers.BoolValue{Value: reverse}
req.Limit = uint32(limit)
req.Offset = uint32(offset)
resp, err := client.Category().DescribeCategories(openpitrix.SystemContext(), req)
resp, err := c.opClient.DescribeCategories(openpitrix.SystemContext(), req)
if err != nil {
klog.Error(err)
return nil, err
@@ -158,7 +146,7 @@ func ListCategories(conditions *params.Conditions, orderBy string, reverse bool,
items := make([]interface{}, 0)
for _, item := range resp.CategorySet {
items = append(items, utils.ConvertCategory(item))
items = append(items, convertCategory(item))
}
return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil

View File

@@ -0,0 +1,49 @@
/*
*
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package openpitrix
import (
"k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
)
type Interface interface {
ApplicationInterface
AppTemplateInterface
AttachmentInterface
CategoryInterface
RepoInterface
}
type openpitrixOperator struct {
ApplicationInterface
AppTemplateInterface
AttachmentInterface
CategoryInterface
RepoInterface
}
func NewOpenpitrixOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) Interface {
return &openpitrixOperator{
ApplicationInterface: newApplicationOperator(informers, opClient),
AppTemplateInterface: newAppTemplateOperator(opClient),
AttachmentInterface: newAttachmentOperator(opClient),
CategoryInterface: newCategoryOperator(opClient),
RepoInterface: newRepoOperator(opClient),
}
}

View File

@@ -1,6 +1,6 @@
/*
*
* Copyright 2019 The KubeSphere Authors.
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
* /
*/
package repo
package openpitrix
import (
"fmt"
@@ -25,21 +25,34 @@ import (
"google.golang.org/grpc/status"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/openpitrix/type"
"kubesphere.io/kubesphere/pkg/models/openpitrix/utils"
"kubesphere.io/kubesphere/pkg/server/params"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"openpitrix.io/openpitrix/pkg/pb"
"strings"
)
func CreateRepo(request *types.CreateRepoRequest) (*types.CreateRepoResponse, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
type RepoInterface interface {
CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error)
DeleteRepo(id string) error
ModifyRepo(id string, request *ModifyRepoRequest) error
DescribeRepo(id string) (*Repo, error)
ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error)
DoRepoAction(repoId string, request *RepoActionRequest) error
ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error)
}
type repoOperator struct {
opClient openpitrix.Client
}
func newRepoOperator(opClient openpitrix.Client) RepoInterface {
return &repoOperator{
opClient: opClient,
}
}
func (c *repoOperator) CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) {
createRepoRequest := &pb.CreateRepoRequest{
Name: &wrappers.StringValue{Value: request.Name},
Description: &wrappers.StringValue{Value: request.Description},
@@ -58,23 +71,18 @@ func CreateRepo(request *types.CreateRepoRequest) (*types.CreateRepoResponse, er
createRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)}
}
resp, err := op.Repo().CreateRepo(openpitrix.SystemContext(), createRepoRequest)
resp, err := c.opClient.CreateRepo(openpitrix.SystemContext(), createRepoRequest)
if err != nil {
klog.Error(err)
return nil, err
}
return &types.CreateRepoResponse{
return &CreateRepoResponse{
RepoID: resp.GetRepoId().GetValue(),
}, nil
}
func DeleteRepo(id string) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
_, err = op.Repo().DeleteRepos(openpitrix.SystemContext(), &pb.DeleteReposRequest{
func (c *repoOperator) DeleteRepo(id string) error {
_, err := c.opClient.DeleteRepos(openpitrix.SystemContext(), &pb.DeleteReposRequest{
RepoId: []string{id},
})
if err != nil {
@@ -84,12 +92,7 @@ func DeleteRepo(id string) error {
return nil
}
func PatchRepo(id string, request *types.ModifyRepoRequest) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *repoOperator) ModifyRepo(id string, request *ModifyRepoRequest) error {
modifyRepoRequest := &pb.ModifyRepoRequest{
RepoId: &wrappers.StringValue{Value: id},
}
@@ -126,7 +129,7 @@ func PatchRepo(id string, request *types.ModifyRepoRequest) error {
modifyRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)}
}
_, err = op.Repo().ModifyRepo(openpitrix.SystemContext(), modifyRepoRequest)
_, err := c.opClient.ModifyRepo(openpitrix.SystemContext(), modifyRepoRequest)
if err != nil {
klog.Error(err)
return err
@@ -134,13 +137,8 @@ func PatchRepo(id string, request *types.ModifyRepoRequest) error {
return nil
}
func DescribeRepo(id string) (*types.Repo, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
resp, err := op.Repo().DescribeRepos(openpitrix.SystemContext(), &pb.DescribeReposRequest{
func (c *repoOperator) DescribeRepo(id string) (*Repo, error) {
resp, err := c.opClient.DescribeRepos(openpitrix.SystemContext(), &pb.DescribeReposRequest{
RepoId: []string{id},
Limit: 1,
})
@@ -149,10 +147,10 @@ func DescribeRepo(id string) (*types.Repo, error) {
return nil, err
}
var repo *types.Repo
var repo *Repo
if len(resp.RepoSet) > 0 {
repo = utils.ConvertRepo(resp.RepoSet[0])
repo = convertRepo(resp.RepoSet[0])
return repo, nil
} else {
err := status.New(codes.NotFound, "resource not found").Err()
@@ -161,41 +159,34 @@ func DescribeRepo(id string) (*types.Repo, error) {
}
}
func ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *repoOperator) ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
req := &pb.DescribeReposRequest{}
if keyword := conditions.Match["keyword"]; keyword != "" {
if keyword := conditions.Match[Keyword]; keyword != "" {
req.SearchWord = &wrappers.StringValue{Value: keyword}
}
if status := conditions.Match["status"]; status != "" {
if status := conditions.Match[Status]; status != "" {
req.Status = strings.Split(status, "|")
}
if typeStr := conditions.Match["type"]; typeStr != "" {
if typeStr := conditions.Match[Type]; typeStr != "" {
req.Type = strings.Split(typeStr, "|")
}
if visibility := conditions.Match["visibility"]; visibility != "" {
if visibility := conditions.Match[Visibility]; visibility != "" {
req.Visibility = strings.Split(visibility, "|")
}
if status := conditions.Match["status"]; status != "" {
if status := conditions.Match[Status]; status != "" {
req.Status = strings.Split(status, "|")
}
if workspace := conditions.Match["workspace"]; workspace != "" {
if workspace := conditions.Match[WorkspaceLabel]; workspace != "" {
req.Label = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", workspace)}
}
if orderBy != "" {
req.SortKey = &wrappers.StringValue{Value: orderBy}
}
req.Reverse = &wrappers.BoolValue{Value: !reverse}
req.Reverse = &wrappers.BoolValue{Value: reverse}
req.Limit = uint32(limit)
req.Offset = uint32(offset)
resp, err := client.Repo().DescribeRepos(openpitrix.SystemContext(), req)
resp, err := c.opClient.DescribeRepos(openpitrix.SystemContext(), req)
if err != nil {
klog.Error(err)
return nil, err
@@ -204,21 +195,14 @@ func ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limi
items := make([]interface{}, 0)
for _, item := range resp.RepoSet {
items = append(items, utils.ConvertRepo(item))
items = append(items, convertRepo(item))
}
return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil
}
func ValidateRepo(request *types.ValidateRepoRequest) (*types.ValidateRepoResponse, error) {
client, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
resp, err := client.Repo().ValidateRepo(openpitrix.SystemContext(), &pb.ValidateRepoRequest{
func (c *repoOperator) ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) {
resp, err := c.opClient.ValidateRepo(openpitrix.SystemContext(), &pb.ValidateRepoRequest{
Type: &wrappers.StringValue{Value: request.Type},
Credential: &wrappers.StringValue{Value: request.Credential},
Url: &wrappers.StringValue{Value: request.Url},
@@ -229,25 +213,20 @@ func ValidateRepo(request *types.ValidateRepoRequest) (*types.ValidateRepoRespon
return nil, err
}
return &types.ValidateRepoResponse{
return &ValidateRepoResponse{
ErrorCode: int64(resp.ErrorCode),
Ok: resp.Ok.Value,
}, nil
}
func DoRepoAction(repoId string, request *types.RepoActionRequest) error {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return err
}
func (c *repoOperator) DoRepoAction(repoId string, request *RepoActionRequest) error {
var err error
switch request.Action {
case "index":
case ActionIndex:
indexRepoRequest := &pb.IndexRepoRequest{
RepoId: &wrappers.StringValue{Value: repoId},
}
_, err := op.RepoIndexer().IndexRepo(openpitrix.SystemContext(), indexRepoRequest)
_, err := c.opClient.IndexRepo(openpitrix.SystemContext(), indexRepoRequest)
if err != nil {
klog.Error(err)
@@ -262,13 +241,7 @@ func DoRepoAction(repoId string, request *types.RepoActionRequest) error {
}
}
func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) {
op, err := cs.ClientSets().OpenPitrix()
if err != nil {
klog.Error(err)
return nil, err
}
func (c *repoOperator) ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) {
describeRepoEventsRequest := &pb.DescribeRepoEventsRequest{
RepoId: []string{repoId},
}
@@ -281,7 +254,7 @@ func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset
describeRepoEventsRequest.Limit = uint32(limit)
describeRepoEventsRequest.Offset = uint32(offset)
resp, err := op.RepoIndexer().DescribeRepoEvents(openpitrix.SystemContext(), describeRepoEventsRequest)
resp, err := c.opClient.DescribeRepoEvents(openpitrix.SystemContext(), describeRepoEventsRequest)
if err != nil {
klog.Error(err)
@@ -291,7 +264,7 @@ func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset
items := make([]interface{}, 0)
for _, item := range resp.RepoEventSet {
items = append(items, utils.ConvertRepoEvent(item))
items = append(items, convertRepoEvent(item))
}
return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil

View File

@@ -1,4 +1,4 @@
package types
package openpitrix
import (
"github.com/go-openapi/strfmt"
@@ -835,3 +835,30 @@ type ModifyClusterAttributesRequest struct {
// cluster name
Name *string `json:"name,omitempty"`
}
const (
CreateTime = "create_time"
StatusTime = "status_time"
RuntimeId = "runtime_id"
VersionId = "version_id"
RepoId = "repo_id"
CategoryId = "category_id"
Status = "status"
Type = "type"
Visibility = "visibility"
AppId = "app_id"
Keyword = "keyword"
ISV = "isv"
WorkspaceLabel = "workspace"
BuiltinRepoId = "repo-helm"
StatusActive = "active"
StatusSuspended = "suspended"
ActionRecover = "recover"
ActionSuspend = "suspend"
ActionCancel = "cancel"
ActionPass = "pass"
ActionReject = "reject"
ActionSubmit = "submit"
ActionRelease = "release"
ActionIndex = "index"
)

View File

@@ -16,29 +16,28 @@
* /
*/
package utils
package openpitrix
import (
"github.com/go-openapi/strfmt"
"kubesphere.io/kubesphere/pkg/models/openpitrix/type"
"openpitrix.io/openpitrix/pkg/pb"
"time"
)
func ConvertApp(in *pb.App) *types.App {
func convertApp(in *pb.App) *App {
if in == nil {
return nil
}
categorySet := make(types.AppCategorySet, 0)
categorySet := make(AppCategorySet, 0)
for _, item := range in.CategorySet {
category := ConvertResourceCategory(item)
category := convertResourceCategory(item)
categorySet = append(categorySet, category)
}
out := types.App{
out := App{
CategorySet: categorySet,
}
@@ -93,7 +92,7 @@ func ConvertApp(in *pb.App) *types.App {
out.Keywords = in.Keywords.Value
}
if in.LatestAppVersion != nil {
out.LatestAppVersion = ConvertAppVersion(in.LatestAppVersion)
out.LatestAppVersion = convertAppVersion(in.LatestAppVersion)
}
if in.Name != nil {
out.Name = in.Name.Value
@@ -131,11 +130,11 @@ func ConvertApp(in *pb.App) *types.App {
return &out
}
func ConvertAppVersion(in *pb.AppVersion) *types.AppVersion {
func convertAppVersion(in *pb.AppVersion) *AppVersion {
if in == nil {
return nil
}
out := types.AppVersion{}
out := AppVersion{}
if in.AppId != nil {
out.AppId = in.AppId.Value
}
@@ -210,11 +209,11 @@ func ConvertAppVersion(in *pb.AppVersion) *types.AppVersion {
}
func ConvertResourceCategory(in *pb.ResourceCategory) *types.ResourceCategory {
func convertResourceCategory(in *pb.ResourceCategory) *ResourceCategory {
if in == nil {
return nil
}
out := types.ResourceCategory{}
out := ResourceCategory{}
if in.CategoryId != nil {
out.CategoryId = in.CategoryId.Value
@@ -240,11 +239,11 @@ func ConvertResourceCategory(in *pb.ResourceCategory) *types.ResourceCategory {
return &out
}
func ConvertCategory(in *pb.Category) *types.Category {
func convertCategory(in *pb.Category) *Category {
if in == nil {
return nil
}
out := types.Category{}
out := Category{}
if in.CategoryId != nil {
out.CategoryID = in.CategoryId.Value
@@ -276,11 +275,11 @@ func ConvertCategory(in *pb.Category) *types.Category {
return &out
}
func ConvertAttachment(in *pb.Attachment) *types.Attachment {
func convertAttachment(in *pb.Attachment) *Attachment {
if in == nil {
return nil
}
out := types.Attachment{}
out := Attachment{}
out.AttachmentID = in.AttachmentId
@@ -298,11 +297,11 @@ func ConvertAttachment(in *pb.Attachment) *types.Attachment {
return &out
}
func ConvertRepo(in *pb.Repo) *types.Repo {
func convertRepo(in *pb.Repo) *Repo {
if in == nil {
return nil
}
out := types.Repo{}
out := Repo{}
if in.RepoId != nil {
out.RepoId = in.RepoId.Value
@@ -317,10 +316,10 @@ func ConvertRepo(in *pb.Repo) *types.Repo {
out.Credential = in.Credential.Value
}
categorySet := make(types.RepoCategorySet, 0)
categorySet := make(RepoCategorySet, 0)
for _, item := range in.CategorySet {
category := ConvertResourceCategory(item)
category := convertResourceCategory(item)
categorySet = append(categorySet, category)
}
@@ -339,10 +338,10 @@ func ConvertRepo(in *pb.Repo) *types.Repo {
out.Description = in.Description.Value
}
labelSet := make(types.RepoLabels, 0)
labelSet := make(RepoLabels, 0)
for _, item := range in.Labels {
label := ConvertRepoLabel(item)
label := convertRepoLabel(item)
labelSet = append(labelSet, label)
}
@@ -358,10 +357,10 @@ func ConvertRepo(in *pb.Repo) *types.Repo {
out.RepoId = in.RepoId.Value
}
selectorSet := make(types.RepoSelectors, 0)
selectorSet := make(RepoSelectors, 0)
for _, item := range in.Selectors {
selector := ConvertRepoSelector(item)
selector := convertRepoSelector(item)
selectorSet = append(selectorSet, selector)
}
@@ -385,11 +384,11 @@ func ConvertRepo(in *pb.Repo) *types.Repo {
return &out
}
func ConvertRepoLabel(in *pb.RepoLabel) *types.RepoLabel {
func convertRepoLabel(in *pb.RepoLabel) *RepoLabel {
if in == nil {
return nil
}
out := types.RepoLabel{}
out := RepoLabel{}
if in.CreateTime != nil {
date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0))
out.CreateTime = &date
@@ -403,11 +402,11 @@ func ConvertRepoLabel(in *pb.RepoLabel) *types.RepoLabel {
return &out
}
func ConvertRepoSelector(in *pb.RepoSelector) *types.RepoSelector {
func convertRepoSelector(in *pb.RepoSelector) *RepoSelector {
if in == nil {
return nil
}
out := types.RepoSelector{}
out := RepoSelector{}
if in.CreateTime != nil {
date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0))
out.CreateTime = &date
@@ -421,11 +420,11 @@ func ConvertRepoSelector(in *pb.RepoSelector) *types.RepoSelector {
return &out
}
func ConvertAppVersionAudit(in *pb.AppVersionAudit) *types.AppVersionAudit {
func convertAppVersionAudit(in *pb.AppVersionAudit) *AppVersionAudit {
if in == nil {
return nil
}
out := types.AppVersionAudit{}
out := AppVersionAudit{}
if in.AppId != nil {
out.AppId = in.AppId.Value
}
@@ -463,11 +462,11 @@ func ConvertAppVersionAudit(in *pb.AppVersionAudit) *types.AppVersionAudit {
return &out
}
func ConvertAppVersionReview(in *pb.AppVersionReview) *types.AppVersionReview {
func convertAppVersionReview(in *pb.AppVersionReview) *AppVersionReview {
if in == nil {
return nil
}
out := types.AppVersionReview{}
out := AppVersionReview{}
if in.AppId != nil {
out.AppId = in.AppId.Value
}
@@ -475,9 +474,9 @@ func ConvertAppVersionReview(in *pb.AppVersionReview) *types.AppVersionReview {
out.AppName = in.AppName.Value
}
if in.Phase != nil {
out.Phase = make(types.AppVersionReviewPhaseOAIGen)
out.Phase = make(AppVersionReviewPhaseOAIGen)
for k, v := range in.Phase {
out.Phase[k] = *ConvertAppVersionReviewPhase(v)
out.Phase[k] = *convertAppVersionReviewPhase(v)
}
}
if in.ReviewId != nil {
@@ -504,11 +503,11 @@ func ConvertAppVersionReview(in *pb.AppVersionReview) *types.AppVersionReview {
return &out
}
func ConvertAppVersionReviewPhase(in *pb.AppVersionReviewPhase) *types.AppVersionReviewPhase {
func convertAppVersionReviewPhase(in *pb.AppVersionReviewPhase) *AppVersionReviewPhase {
if in == nil {
return nil
}
out := types.AppVersionReviewPhase{}
out := AppVersionReviewPhase{}
if in.Message != nil {
out.Message = in.Message.Value
}
@@ -532,11 +531,11 @@ func ConvertAppVersionReviewPhase(in *pb.AppVersionReviewPhase) *types.AppVersio
return &out
}
func ConvertRepoEvent(in *pb.RepoEvent) *types.RepoEvent {
func convertRepoEvent(in *pb.RepoEvent) *RepoEvent {
if in == nil {
return nil
}
out := types.RepoEvent{}
out := RepoEvent{}
if in.CreateTime != nil {
date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0))
out.CreateTime = &date
@@ -564,11 +563,11 @@ func ConvertRepoEvent(in *pb.RepoEvent) *types.RepoEvent {
return &out
}
func ConvertCluster(in *pb.Cluster) *types.Cluster {
func convertCluster(in *pb.Cluster) *Cluster {
if in == nil {
return nil
}
out := types.Cluster{}
out := Cluster{}
if in.AdditionalInfo != nil {
out.AdditionalInfo = in.AdditionalInfo.Value
}

View File

@@ -52,6 +52,10 @@ type ResourceGetter struct {
resourcesGetters map[string]v1alpha2.Interface
}
func (r ResourceGetter) Add(resource string, getter v1alpha2.Interface) {
r.resourcesGetters[resource] = getter
}
func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter {
resourceGetters := make(map[string]v1alpha2.Interface)

View File

@@ -28,6 +28,7 @@ import (
"kubesphere.io/kubesphere/pkg/gojenkins/utils"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/params"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"net/http"
@@ -317,7 +318,7 @@ func CreateDevopsProject(username string, workspace string, req *v1alpha2.DevOps
return project, nil
}
func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, error) {
func GetUserDevopsSimpleRules(username, projectId string) ([]iam.SimpleRule, error) {
role, err := devops.GetProjectUserRole(username, projectId)
if err != nil {
klog.Errorf("%+v", err)
@@ -326,12 +327,12 @@ func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule,
return GetDevopsRoleSimpleRules(role), nil
}
func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
var rules []models.SimpleRule
func GetDevopsRoleSimpleRules(role string) []iam.SimpleRule {
var rules []iam.SimpleRule
switch role {
case "developer":
rules = []models.SimpleRule{
rules = []iam.SimpleRule{
{Name: "pipelines", Actions: []string{"view", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
@@ -339,7 +340,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
}
break
case "owner":
rules = []models.SimpleRule{
rules = []iam.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"create", "edit", "view", "delete"}},
@@ -348,7 +349,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
}
break
case "maintainer":
rules = []models.SimpleRule{
rules = []iam.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
@@ -359,7 +360,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
case "reporter":
fallthrough
default:
rules = []models.SimpleRule{
rules = []iam.SimpleRule{
{Name: "pipelines", Actions: []string{"view"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},

View File

@@ -21,9 +21,10 @@ 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"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params"
@@ -32,11 +33,34 @@ import (
"strings"
)
type namespaceSearcher struct {
type NamespaceInterface interface {
Search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error)
CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error)
}
// Exactly Match
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
type namespaceSearcher struct {
k8s kubernetes.Interface
informers k8sinformers.SharedInformerFactory
}
func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error) {
if namespace.Labels == nil {
namespace.Labels = make(map[string]string, 0)
}
if username != "" {
namespace.Annotations[constants.CreatorAnnotationKey] = username
}
namespace.Labels[constants.WorkspaceLabelKey] = workspace
return s.k8s.CoreV1().Namespaces().Create(namespace)
}
func newNamespaceOperator(k8s kubernetes.Interface, informers k8sinformers.SharedInformerFactory) NamespaceInterface {
return &namespaceSearcher{k8s: k8s, informers: informers}
}
func (s *namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
for k, v := range match {
switch k {
case v1alpha2.Name:
@@ -58,7 +82,7 @@ func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) boo
return true
}
func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool {
func (s *namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool {
for k, v := range fuzzy {
switch k {
@@ -74,7 +98,7 @@ func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) boo
return true
}
func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
func (s *namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
switch orderBy {
case "createTime":
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
@@ -85,7 +109,7 @@ func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
}
}
func (*namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) {
func (s *namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) {
roles, err := iam.GetUserRoles("", username)
@@ -93,7 +117,7 @@ func (*namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error
return nil, err
}
namespaces := make([]*v1.Namespace, 0)
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
namespaceLister := s.informers.Core().V1().Namespaces().Lister()
for _, role := range roles {
namespace, err := namespaceLister.Get(role.Namespace)
if err != nil {
@@ -117,7 +141,7 @@ func containsNamespace(namespaces []*v1.Namespace, namespace *v1.Namespace) bool
return false
}
func (s *namespaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) {
func (s *namespaceSearcher) Search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) {
rules, err := iam.GetUserClusterRules(username)
@@ -128,7 +152,7 @@ func (s *namespaceSearcher) search(username string, conditions *params.Condition
namespaces := make([]*v1.Namespace, 0)
if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"namespaces"}}) {
namespaces, err = informers.SharedInformerFactory().Core().V1().Namespaces().Lister().List(labels.Everything())
namespaces, err = s.informers.Core().V1().Namespaces().Lister().List(labels.Everything())
} else {
namespaces, err = s.GetNamespaces(username)
}
@@ -148,12 +172,14 @@ func (s *namespaceSearcher) search(username string, conditions *params.Condition
// order & reverse
sort.Slice(result, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
i, j = j, i
}
return s.compare(result[i], result[j], orderBy)
})
return result, nil
}
func CreateNamespace() {
}

View File

@@ -19,49 +19,62 @@ package tenant
import (
"k8s.io/api/core/v1"
k8sinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
ws "kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"strconv"
)
var (
workspaces = workspaceSearcher{}
namespaces = namespaceSearcher{}
)
func CreateNamespace(workspaceName string, namespace *v1.Namespace, username string) (*v1.Namespace, error) {
if namespace.Labels == nil {
namespace.Labels = make(map[string]string, 0)
}
if username != "" {
namespace.Annotations[constants.CreatorAnnotationKey] = username
}
namespace.Labels[constants.WorkspaceLabelKey] = workspaceName
return client.ClientSets().K8s().Kubernetes().CoreV1().Namespaces().Create(namespace)
type Interface interface {
CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error)
DeleteNamespace(workspace, namespace string) error
DescribeWorkspace(username, workspace string) (*v1alpha1.Workspace, error)
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)
}
func DescribeWorkspace(username, workspaceName string) (*v1alpha1.Workspace, error) {
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
type tenantOperator struct {
workspaces WorkspaceInterface
namespaces NamespaceInterface
}
func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error {
return t.workspaces.DeleteNamespace(workspace, namespace)
}
func New(client kubernetes.Interface, informers k8sinformers.SharedInformerFactory, ksinformers ksinformers.SharedInformerFactory, db *mysql.Database) Interface {
return &tenantOperator{
workspaces: newWorkspaceOperator(client, informers, ksinformers, db),
namespaces: newNamespaceOperator(client, informers),
}
}
func (t *tenantOperator) CreateNamespace(workspaceName string, namespace *v1.Namespace, username string) (*v1.Namespace, error) {
return t.namespaces.CreateNamespace(workspaceName, namespace, username)
}
func (t *tenantOperator) DescribeWorkspace(username, workspaceName string) (*v1alpha1.Workspace, error) {
workspace, err := t.workspaces.GetWorkspace(workspaceName)
if err != nil {
return nil, err
}
workspace = appendAnnotations(username, workspace)
if username != "" {
workspace = t.appendAnnotations(username, workspace)
}
return workspace, nil
}
func ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
func (t *tenantOperator) ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
workspaces, err := workspaces.search(username, conditions, orderBy, reverse)
workspaces, err := t.workspaces.SearchWorkspace(username, conditions, orderBy, reverse)
if err != nil {
return nil, err
@@ -71,7 +84,7 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri
result := make([]interface{}, 0)
for i, workspace := range workspaces {
if len(result) < limit && i >= offset {
workspace := appendAnnotations(username, workspace)
workspace := t.appendAnnotations(username, workspace)
result = append(result, workspace)
}
}
@@ -79,12 +92,12 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri
return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil
}
func appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace {
func (t *tenantOperator) appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace {
workspace = workspace.DeepCopy()
if workspace.Annotations == nil {
workspace.Annotations = make(map[string]string)
}
ns, err := ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0)
ns, err := t.ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0)
if err == nil {
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
}
@@ -92,16 +105,18 @@ func appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1
if err == nil {
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
}
userCount, err := ws.WorkspaceUserCount(workspace.Name)
userCount, err := t.workspaces.CountUsersInWorkspace(workspace.Name)
if err == nil {
workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount)
}
return workspace
}
func ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
func (t *tenantOperator) ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
namespaces, err := namespaces.search(username, conditions, orderBy, reverse)
namespaces, err := t.namespaces.Search(username, conditions, orderBy, reverse)
if err != nil {
return nil, err

View File

@@ -1,41 +1,241 @@
/*
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.
*/
*
* Copyright 2020 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package tenant
import (
"fmt"
core "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/labels"
"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/informers"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params"
clientset "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"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"
)
type workspaceSearcher struct {
type WorkspaceInterface interface {
GetWorkspace(workspace string) (*v1alpha1.Workspace, error)
SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error)
ListNamespaces(workspace string) ([]*core.Namespace, error)
DeleteNamespace(workspace, namespace string) error
RemoveUser(user, workspace string) error
AddUser(workspace string, user *iam.User) error
CountDevopsProjectsInWorkspace(workspace string) (int, error)
CountUsersInWorkspace(workspace string) (int, error)
CountOrgRoles() (int, error)
CountWorkspaces() (int, error)
CountNamespacesInWorkspace(workspace string) (int, error)
}
// Exactly Match
func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspace) bool {
type workspaceOperator struct {
client kubernetes.Interface
informers informers.SharedInformerFactory
ksInformers externalversions.SharedInformerFactory
// 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, db *mysql.Database) WorkspaceInterface {
return &workspaceOperator{
client: client,
informers: informers,
ksInformers: ksinformers,
db: db,
}
}
func (w *workspaceOperator) ListNamespaces(workspace string) ([]*core.Namespace, error) {
namespaces, err := w.informers.Core().V1().Namespaces().Lister().List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspace}))
if err != nil {
return nil, err
}
return namespaces, nil
}
func (w *workspaceOperator) DeleteNamespace(workspace string, namespace string) error {
ns, err := w.informers.Core().V1().Namespaces().Lister().Get(namespace)
if err != nil {
return err
}
if ns.Labels[constants.WorkspaceLabelKey] == workspace {
deletePolicy := metav1.DeletePropagationBackground
return w.client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
} else {
return apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "workspace"}, workspace)
}
}
func (w *workspaceOperator) RemoveUser(workspace string, username string) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username)
if err != nil {
return err
}
err = w.deleteWorkspaceRoleBinding(workspace, username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey])
if err != nil {
return err
}
return nil
}
func (w *workspaceOperator) AddUser(workspaceName string, user *iam.User) error {
workspaceRole, err := iam.GetUserWorkspaceRole(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 !k8sutil.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
}
func (w *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string) (int, error) {
if w.db == nil {
return 0, clientset.ErrClientSetNotEnabled
}
query := w.db.Select(devops.DevOpsProjectIdColumn).
From(devops.DevOpsProjectTableName).
Where(db.And(db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspaceName),
db.Eq(devops.StatusColumn, devops.StatusActive)))
devOpsProjects := make([]string, 0)
if _, err := query.Load(&devOpsProjects); err != nil {
return 0, err
}
return len(devOpsProjects), nil
}
func (w *workspaceOperator) CountUsersInWorkspace(workspace string) (int, error) {
count, err := iam.WorkspaceUsersTotalCount(workspace)
if err != nil {
return 0, err
}
return count, nil
}
func (w *workspaceOperator) CountOrgRoles() (int, error) {
return len(constants.WorkSpaceRoles), nil
}
func (w *workspaceOperator) CountNamespacesInWorkspace(workspace string) (int, error) {
ns, err := w.ListNamespaces(workspace)
if err != nil {
return 0, err
}
return len(ns), nil
}
func (*workspaceOperator) match(match map[string]string, item *v1alpha1.Workspace) bool {
for k, v := range match {
switch k {
case v1alpha2.Name:
@@ -57,7 +257,7 @@ func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspac
return true
}
func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspace) bool {
func (*workspaceOperator) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspace) bool {
for k, v := range fuzzy {
switch k {
@@ -73,7 +273,7 @@ func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspac
return true
}
func (*workspaceSearcher) compare(a, b *v1alpha1.Workspace, orderBy string) bool {
func (*workspaceOperator) compare(a, b *v1alpha1.Workspace, orderBy string) bool {
switch orderBy {
case v1alpha2.CreateTime:
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
@@ -84,7 +284,7 @@ func (*workspaceSearcher) compare(a, b *v1alpha1.Workspace, orderBy string) bool
}
}
func (s *workspaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) {
func (w *workspaceOperator) SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) {
rules, err := iam.GetUserClusterRules(username)
if err != nil {
@@ -94,7 +294,7 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition
workspaces := make([]*v1alpha1.Workspace, 0)
if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"workspaces"}}) {
workspaces, err = informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything())
workspaces, err = w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything())
if err != nil {
return nil, err
}
@@ -104,7 +304,7 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition
return nil, err
}
for k := range workspaceRoles {
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(k)
workspace, err := w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().Get(k)
if err != nil {
return nil, err
}
@@ -115,7 +315,7 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition
result := make([]*v1alpha1.Workspace, 0)
for _, workspace := range workspaces {
if s.match(conditions.Match, workspace) && s.fuzzy(conditions.Fuzzy, workspace) {
if w.match(conditions.Match, workspace) && w.fuzzy(conditions.Fuzzy, workspace) {
result = append(result, workspace)
}
}
@@ -123,18 +323,16 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition
// order & reverse
sort.Slice(result, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
i, j = j, i
}
return s.compare(result[i], result[j], orderBy)
return w.compare(result[i], result[j], orderBy)
})
return result, nil
}
func GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
func (w *workspaceOperator) GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
return w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
}
func contains(m map[string]string, key, value string) bool {
@@ -149,3 +347,47 @@ func contains(m map[string]string, key, value string) bool {
}
return false
}
/*
// TODO: move to metrics package
func GetAllProjectNums() (int, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
list, err := namespaceLister.List(labels.Everything())
if err != nil {
return 0, err
}
return len(list), nil
}
func GetAllDevOpsProjectsNums() (int, error) {
_, err := clientset.ClientSets().Devops()
if _, notEnabled := err.(clientset.ClientSetNotEnabledError); notEnabled {
return 0, err
}
dbconn, err := clientset.ClientSets().MySQL()
if err != nil {
return 0, err
}
query := dbconn.Select(devops.DevOpsProjectIdColumn).
From(devops.DevOpsProjectTableName).
Where(db.Eq(devops.StatusColumn, devops.StatusActive))
devOpsProjects := make([]string, 0)
if _, err := query.Load(&devOpsProjects); err != nil {
return 0, err
}
return len(devOpsProjects), nil
}
*/
func (w *workspaceOperator) CountWorkspaces() (int, error) {
ws, err := w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything())
if err != nil {
return 0, err
}
return len(ws), nil
}

View File

@@ -17,12 +17,6 @@
*/
package models
import (
"time"
"k8s.io/api/rbac/v1"
)
type PageableResponse struct {
Items []interface{} `json:"items" description:"paging data"`
TotalCount int `json:"total_count" description:"total count"`
@@ -35,41 +29,6 @@ type Workspace struct {
DevopsProjects []string `json:"devops_projects"`
}
type Action struct {
Name string `json:"name"`
Rules []v1.PolicyRule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Actions []Action `json:"actions"`
}
type SimpleRule struct {
Name string `json:"name" description:"rule name"`
Actions []string `json:"actions" description:"actions"`
}
type User struct {
Username string `json:"username"`
Email string `json:"email"`
Lang string `json:"lang,omitempty"`
Description string `json:"description"`
CreateTime time.Time `json:"create_time"`
Groups []string `json:"groups,omitempty"`
Password string `json:"password,omitempty"`
CurrentPassword string `json:"current_password,omitempty"`
AvatarUrl string `json:"avatar_url"`
LastLoginTime string `json:"last_login_time"`
Status int `json:"status"`
ClusterRole string `json:"cluster_role"`
Roles map[string]string `json:"roles,omitempty"`
Role string `json:"role,omitempty"`
RoleBinding string `json:"role_binding,omitempty"`
RoleBindTime *time.Time `json:"role_bind_time,omitempty"`
WorkspaceRole string `json:"workspace_role,omitempty"`
}
type Group struct {
Path string `json:"path"`
Name string `json:"name"`

View File

@@ -1,276 +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 workspaces
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam"
clientset "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"strings"
core "k8s.io/api/core/v1"
"errors"
"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"
)
type Interface interface {
ListNamespaces(workspace string) ([]*core.Namespace, error)
DeleteNamespace(workspace, namespace string) error
RemoveUser(user, workspace string) error
AddUser(workspace string, user *models.User) error
CountDevopsProjectsInWorkspace(workspace string) (int, error)
CountUsersInWorkspace(workspace string) (int, error)
CountOrgRoles() (int, error)
CountWorkspaces() (int, error)
CountNamespacesInWorkspace(workspace string) (int, error)
}
type workspaceOperator struct {
client kubernetes.Interface
informers informers.SharedInformerFactory
ksInformers externalversions.SharedInformerFactory
// 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, db *mysql.Database) Interface {
return &workspaceOperator{
client: client,
informers: informers,
ksInformers: ksinformers,
db: db,
}
}
func (c *workspaceOperator) ListNamespaces(workspace string) ([]*core.Namespace, error) {
namespaces, err := c.informers.Core().V1().Namespaces().Lister().List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspace}))
if err != nil {
return nil, err
}
return namespaces, nil
}
func (c *workspaceOperator) DeleteNamespace(workspace string, namespace string) error {
ns, err := c.informers.Core().V1().Namespaces().Lister().Get(namespace)
if err != nil {
return err
}
if ns.Labels[constants.WorkspaceLabelKey] == workspace {
deletePolicy := metav1.DeletePropagationBackground
return c.client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
} else {
return errors.New("resource not found")
}
}
func (c *workspaceOperator) RemoveUser(workspace string, username string) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username)
if err != nil {
return err
}
err = c.deleteWorkspaceRoleBinding(workspace, username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey])
if err != nil {
return err
}
return nil
}
func (c *workspaceOperator) AddUser(workspaceName string, user *models.User) error {
workspaceRole, err := iam.GetUserWorkspaceRole(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 := c.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 c.createWorkspaceRoleBinding(workspaceName, user.Username, user.WorkspaceRole)
}
func (c *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 := c.informers.Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
if err != nil {
return err
}
if !k8sutil.ContainsUser(workspaceRoleBinding.Subjects, username) {
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects, v1.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: username})
_, err = c.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding)
if err != nil {
klog.Errorf("update workspace role binding failed: %+v", err)
return err
}
}
return nil
}
func (c *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 := c.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 = c.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding)
return err
}
func (c *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string) (int, error) {
if c.db == nil {
return 0, clientset.ErrClientSetNotEnabled
}
query := c.db.Select(devops.DevOpsProjectIdColumn).
From(devops.DevOpsProjectTableName).
Where(db.And(db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspaceName),
db.Eq(devops.StatusColumn, devops.StatusActive)))
devOpsProjects := make([]string, 0)
if _, err := query.Load(&devOpsProjects); err != nil {
return 0, err
}
return len(devOpsProjects), nil
}
func (c *workspaceOperator) CountUsersInWorkspace(workspace string) (int, error) {
count, err := iam.WorkspaceUsersTotalCount(workspace)
if err != nil {
return 0, err
}
return count, nil
}
func (c *workspaceOperator) CountOrgRoles() (int, error) {
return len(constants.WorkSpaceRoles), nil
}
func (c *workspaceOperator) CountNamespacesInWorkspace(workspace string) (int, error) {
ns, err := c.ListNamespaces(workspace)
if err != nil {
return 0, err
}
return len(ns), nil
}
/*
// TODO: move to metrics package
func GetAllProjectNums() (int, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
list, err := namespaceLister.List(labels.Everything())
if err != nil {
return 0, err
}
return len(list), nil
}
func GetAllDevOpsProjectsNums() (int, error) {
_, err := clientset.ClientSets().Devops()
if _, notEnabled := err.(clientset.ClientSetNotEnabledError); notEnabled {
return 0, err
}
dbconn, err := clientset.ClientSets().MySQL()
if err != nil {
return 0, err
}
query := dbconn.Select(devops.DevOpsProjectIdColumn).
From(devops.DevOpsProjectTableName).
Where(db.Eq(devops.StatusColumn, devops.StatusActive))
devOpsProjects := make([]string, 0)
if _, err := query.Load(&devOpsProjects); err != nil {
return 0, err
}
return len(devOpsProjects), nil
}
*/
func (c *workspaceOperator) CountWorkspaces() (int, error) {
ws, err := c.ksInformers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything())
if err != nil {
return 0, err
}
return len(ws), nil
}